temporal-react-hook
Advanced tools
+30
-9
@@ -10,7 +10,20 @@ # Changelog | ||
| ## [1.6.11] - 2025-06-03 | ||
| ## [1.6.13] - 2025-06-09 | ||
| ### Added | ||
| - `useIsSame` hook now accepts an optional `timeZone` parameter for time zone-aware comparisons. | ||
| - `DemoUseIsSame` component updated to include a time zone selector in the UI. | ||
| - `DemoUseIsThisWeek` component updated for UI consistency (removed date buttons, added datetime-local input) and integrated time zone selector. | ||
| - Enhanced error handling in `useTemporalAdd` hook | ||
| - Comprehensive validation for date and duration inputs | ||
| - Clear error messages with examples and valid options | ||
| - Type checking for duration values | ||
| ### Changed | ||
| - Updated `DemoUseTemporalAdd` component | ||
| - Added inputs for all duration units (years, months, days, hours, minutes, seconds) | ||
| - Improved documentation with examples of combining multiple duration units | ||
| - Enhanced UI layout and styling for better user experience | ||
| ## [1.6.12] - 2025-06-04 | ||
| ### Added | ||
| - Dynamic formatting support in `useTemporalFormat` hook | ||
@@ -20,9 +33,17 @@ - Format dates based on temporal distance (recent, today, this week, etc.) | ||
| - Maintains backward compatibility with existing preset formats | ||
| - UI consistency and styling improvements to `DemoUseTemporalFormat` | ||
| - Updated layout classes for consistent styling | ||
| - Replaced locale selection buttons with dropdown | ||
| - Added dynamic formatting mode with example | ||
| - Improved code block formatting | ||
| ### Fixed | ||
| - Fixed date conversion issues between Temporal and JavaScript Date objects | ||
| - Improved handling of PlainDate objects in useTemporalFormat | ||
| ### Changed | ||
| - Removed direct Temporal API usage from demo components | ||
| - Updated documentation with dynamic formatting examples | ||
| ## [1.6.11] - 2025-06-03 | ||
| ### Added | ||
| - `useIsSame` hook now accepts an optional `timeZone` parameter for time zone-aware comparisons. | ||
| - `DemoUseIsSame` component updated to include a time zone selector in the UI. | ||
| - `DemoUseIsThisWeek` component updated for UI consistency (removed date buttons, added datetime-local input) and integrated time zone selector. | ||
| ### Changed | ||
| - Removed direct `@js-temporal/polyfill` imports from `DemoUseIsSame` and `DemoUseIsThisWeek` demo components for cleaner code. | ||
@@ -29,0 +50,0 @@ - `DemoUseTemporalFormat` component updated for UI consistency (layout classes, locale dropdown) and improved user experience. |
@@ -8,3 +8,3 @@ import { useState } from "react"; | ||
| const [baseDate, setBaseDate] = useState(currentDateTime); | ||
| const [amount, setAmount] = useState({ days: 1 }); | ||
| const [amount, setAmount] = useState({ years: 0, months: 0, days: 1, hours: 0, minutes: 0, seconds: 0 }); | ||
| const add = useTemporalAdd(); | ||
@@ -16,19 +16,87 @@ const result = add(baseDate, amount); | ||
| <h3>useTemporalAdd</h3> | ||
| <div className="demo-row"> | ||
| <b>Base Date:</b> <span className="demo-value">{baseDate.toString()}</span> | ||
| <button className="demo-select-btn" onClick={() => setBaseDate(currentDateTime)}>Now</button> | ||
| <div className="demo-config-panel"> | ||
| <div className="demo-config-row"> | ||
| <b>Base Date:</b> | ||
| <span className="demo-value">{baseDate.toString()}</span> | ||
| <button onClick={() => setBaseDate(currentDateTime)}>Now</button> | ||
| <button onClick={() => setBaseDate(baseDate.add({ days: 1 }))}>+1 day</button> | ||
| <button onClick={() => setBaseDate(baseDate.subtract({ days: 1 }))}>-1 day</button> | ||
| </div> | ||
| </div> | ||
| <div className="demo-row"> | ||
| <b>Days to Add:</b> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.days} | ||
| onChange={e => setAmount({ days: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| <div className="demo-config-panel"> | ||
| <div className="demo-config-row"> | ||
| <b>Duration:</b> | ||
| <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}> | ||
| <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> | ||
| <span>Years:</span> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.years} | ||
| onChange={e => setAmount({ ...amount, years: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| </label> | ||
| <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> | ||
| <span>Months:</span> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.months} | ||
| onChange={e => setAmount({ ...amount, months: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| </label> | ||
| <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> | ||
| <span>Days:</span> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.days} | ||
| onChange={e => setAmount({ ...amount, days: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| </label> | ||
| <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> | ||
| <span>Hours:</span> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.hours} | ||
| onChange={e => setAmount({ ...amount, hours: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| </label> | ||
| <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> | ||
| <span>Minutes:</span> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.minutes} | ||
| onChange={e => setAmount({ ...amount, minutes: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| </label> | ||
| <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> | ||
| <span>Seconds:</span> | ||
| <input | ||
| type="number" | ||
| min={0} | ||
| value={amount.seconds} | ||
| onChange={e => setAmount({ ...amount, seconds: Math.max(0, Number(e.target.value)) })} | ||
| className="demo-input-compact" | ||
| /> | ||
| </label> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div className="demo-row"> | ||
| <b>Result:</b> <span className="demo-value">{result.toString()}</span> | ||
| <div className="demo-config-panel"> | ||
| <div className="demo-config-row"> | ||
| <b>Result:</b> | ||
| <span className="demo-value">{result.toString()}</span> | ||
| </div> | ||
| </div> | ||
| <div className="demo-info-card"> | ||
@@ -41,3 +109,3 @@ <div className="demo-description"> | ||
| <span> | ||
| <strong>Syntax:</strong> useTemporalAdd()<br/> | ||
| <strong>Syntax:</strong> useTemporalAdd(date, amount)<br/> | ||
| <strong>Parameters:</strong><br/> | ||
@@ -47,10 +115,19 @@ - date: Temporal.PlainDateTime — The base date/time<br/> | ||
| <strong>Returns:</strong> Temporal.PlainDateTime — The new date/time after addition<br/> | ||
| <strong>Example:</strong><br/> | ||
| <code> | ||
| import { useTemporalAdd, useTemporalDateTime } from 'temporal-react-hook';<br/> | ||
| <br/> | ||
| const add = useTemporalAdd();<br/> | ||
| const now = useTemporalDateTime();<br/> | ||
| const tomorrow = add(now, { days: 1 });<br/> | ||
| // tomorrow is now plus one day<br/> | ||
| <strong>Example:</strong> | ||
| <code className="example-code"> | ||
| <pre style={{ margin: 0 }}>{`import { useTemporalAdd, useTemporalDateTime } from 'temporal-react-hook'; | ||
| const add = useTemporalAdd(); | ||
| const now = useTemporalDateTime(); | ||
| // Add multiple duration units at once | ||
| const futureDate = add(now, { | ||
| years: 1, // Add 1 year | ||
| months: 6, // and 6 months | ||
| days: 15, // and 15 days | ||
| hours: 3, // and 3 hours | ||
| minutes: 30 // and 30 minutes | ||
| }); | ||
| // Or just add days | ||
| const tomorrow = add(now, { days: 1 });`}</pre> | ||
| </code> | ||
@@ -57,0 +134,0 @@ </span> |
@@ -1,2 +0,2 @@ | ||
| import { useState } from 'react'; | ||
| import { useState } from "react"; | ||
| import useTemporalFormat from '../../src/useTemporalFormat'; | ||
@@ -3,0 +3,0 @@ import useTemporalDateTime from '../../src/useTemporalDateTime'; |
+1
-1
| { | ||
| "name": "temporal-react-hook", | ||
| "version": "1.6.12", | ||
| "version": "1.6.13", | ||
| "description": "A React library that provides hooks for handling date and time operations using the Temporal API", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -7,8 +7,58 @@ import { useCallback } from "react"; | ||
| * Returns a function to add a specified amount of seconds, minutes, hours, days, weeks, months, or years to a Temporal.PlainDateTime. | ||
| * Example: const add = useTemporalAdd(); add(date, { days: 1, hours: 2 }) | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const add = useTemporalAdd(); | ||
| * | ||
| * // Add multiple duration units | ||
| * const futureDate = add(now, { | ||
| * years: 1, // Add 1 year | ||
| * months: 6, // and 6 months | ||
| * days: 15, // and 15 days | ||
| * hours: 3 // and 3 hours | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export default function useTemporalAdd() { | ||
| return useCallback((date: Temporal.PlainDateTime, amount: Partial<Temporal.DurationLike>) => { | ||
| return date.add(amount); | ||
| // Validate date input | ||
| if (!date) { | ||
| throw new Error('Date is required. Please provide a valid Temporal.PlainDateTime object.'); | ||
| } | ||
| if (!(date instanceof Temporal.PlainDateTime)) { | ||
| throw new Error('Invalid date type. Expected Temporal.PlainDateTime but received: ' + | ||
| ((date as any)?.constructor?.name || typeof date)); | ||
| } | ||
| // Validate amount input | ||
| if (!amount || typeof amount !== 'object') { | ||
| throw new Error('Duration amount is required. Please provide an object with duration values ' + | ||
| '(e.g., { days: 1, hours: 2 })'); | ||
| } | ||
| // Validate duration values | ||
| const validKeys = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; | ||
| const invalidKeys = Object.keys(amount).filter(key => !validKeys.includes(key)); | ||
| if (invalidKeys.length > 0) { | ||
| throw new Error(`Invalid duration units found: ${invalidKeys.join(', ')}. ` + | ||
| `Valid units are: ${validKeys.join(', ')}`); | ||
| } | ||
| // Validate duration values are numbers | ||
| for (const [key, value] of Object.entries(amount)) { | ||
| if (typeof value !== 'number') { | ||
| throw new Error(`Invalid value for ${key}: ${value}. Duration values must be numbers.`); | ||
| } | ||
| } | ||
| try { | ||
| return date.add(amount); | ||
| } catch (error) { | ||
| const err = error as Error; | ||
| throw new Error(`Failed to add duration: ${err.message}. ` + | ||
| 'Please check that your duration values are within valid ranges.'); | ||
| } | ||
| }, []); | ||
| } |
360259
1.49%8469
1.39%