@panneau/fields 5.0.0-alpha.15 → 5.0.0-alpha.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +53 -3
  2. package/dist/address/AddressField.css +44 -0
  3. package/dist/address/AddressField.d.ts +37 -0
  4. package/dist/address/AddressField.d.ts.map +1 -0
  5. package/dist/address/AddressField.js +411 -0
  6. package/dist/address/AddressField.js.map +1 -0
  7. package/dist/address/AddressField.module.js +22 -0
  8. package/dist/address/AddressField.module.js.map +1 -0
  9. package/dist/address/address.d.ts +23 -0
  10. package/dist/address/address.d.ts.map +1 -0
  11. package/dist/address/address.js +58 -0
  12. package/dist/address/address.js.map +1 -0
  13. package/dist/address/index.d.ts +3 -0
  14. package/dist/address/index.d.ts.map +1 -0
  15. package/dist/address/index.js +3 -0
  16. package/dist/color/ColorField.css +9 -0
  17. package/dist/color/ColorField.d.ts +10 -3
  18. package/dist/color/ColorField.d.ts.map +1 -1
  19. package/dist/color/ColorField.js +341 -81
  20. package/dist/color/ColorField.js.map +1 -1
  21. package/dist/color/ColorField.module.js +4 -2
  22. package/dist/color/ColorField.module.js.map +1 -1
  23. package/dist/color/index.d.ts +1 -0
  24. package/dist/color/index.d.ts.map +1 -1
  25. package/dist/color/index.js +3 -1
  26. package/dist/color/palette.d.ts +23 -0
  27. package/dist/color/palette.d.ts.map +1 -0
  28. package/dist/color/palette.js +34 -0
  29. package/dist/color/palette.js.map +1 -0
  30. package/dist/country/CountryField.css +42 -0
  31. package/dist/country/CountryField.d.ts +19 -4
  32. package/dist/country/CountryField.d.ts.map +1 -1
  33. package/dist/country/CountryField.js +355 -82
  34. package/dist/country/CountryField.js.map +1 -1
  35. package/dist/country/CountryField.module.js +20 -0
  36. package/dist/country/CountryField.module.js.map +1 -0
  37. package/dist/country/index.d.ts +1 -1
  38. package/dist/country/index.d.ts.map +1 -1
  39. package/dist/daterange/DateRangeField.css +9 -0
  40. package/dist/daterange/DateRangeField.d.ts +31 -0
  41. package/dist/daterange/DateRangeField.d.ts.map +1 -0
  42. package/dist/daterange/DateRangeField.js +372 -0
  43. package/dist/daterange/DateRangeField.js.map +1 -0
  44. package/dist/daterange/DateRangeField.module.js +12 -0
  45. package/dist/daterange/DateRangeField.module.js.map +1 -0
  46. package/dist/daterange/index.d.ts +3 -0
  47. package/dist/daterange/index.d.ts.map +1 -0
  48. package/dist/daterange/index.js +3 -0
  49. package/dist/daterange/range.d.ts +61 -0
  50. package/dist/daterange/range.d.ts.map +1 -0
  51. package/dist/daterange/range.js +119 -0
  52. package/dist/daterange/range.js.map +1 -0
  53. package/dist/duration/DurationField.css +1 -1
  54. package/dist/duration/DurationField.d.ts +54 -13
  55. package/dist/duration/DurationField.d.ts.map +1 -1
  56. package/dist/duration/DurationField.js +205 -159
  57. package/dist/duration/DurationField.js.map +1 -1
  58. package/dist/duration/DurationField.module.js.map +1 -1
  59. package/dist/duration/index.d.ts +1 -1
  60. package/dist/duration/index.d.ts.map +1 -1
  61. package/dist/duration/index.js +1 -1
  62. package/dist/embed/EmbedField.d.ts +11 -2
  63. package/dist/embed/EmbedField.d.ts.map +1 -1
  64. package/dist/embed/EmbedField.js +179 -116
  65. package/dist/embed/EmbedField.js.map +1 -1
  66. package/dist/link/LinkField.css +12 -0
  67. package/dist/link/LinkField.d.ts +38 -0
  68. package/dist/link/LinkField.d.ts.map +1 -0
  69. package/dist/link/LinkField.js +742 -0
  70. package/dist/link/LinkField.js.map +1 -0
  71. package/dist/link/LinkField.module.js +8 -0
  72. package/dist/link/LinkField.module.js.map +1 -0
  73. package/dist/link/index.d.ts +3 -0
  74. package/dist/link/index.d.ts.map +1 -0
  75. package/dist/link/index.js +3 -0
  76. package/dist/link/link.d.ts +30 -0
  77. package/dist/link/link.d.ts.map +1 -0
  78. package/dist/link/link.js +63 -0
  79. package/dist/link/link.js.map +1 -0
  80. package/dist/link/pages.d.ts +17 -0
  81. package/dist/link/pages.d.ts.map +1 -0
  82. package/dist/link/pages.js +104 -0
  83. package/dist/link/pages.js.map +1 -0
  84. package/dist/location/LocationField.d.ts.map +1 -1
  85. package/dist/location/LocationField.js +9 -85
  86. package/dist/location/LocationField.js.map +1 -1
  87. package/dist/location/nominatim.d.ts +8 -2
  88. package/dist/location/nominatim.d.ts.map +1 -1
  89. package/dist/location/nominatim.js +32 -3
  90. package/dist/location/nominatim.js.map +1 -1
  91. package/dist/location/search.d.ts +10 -0
  92. package/dist/location/search.d.ts.map +1 -0
  93. package/dist/location/search.js +83 -0
  94. package/dist/location/search.js.map +1 -0
  95. package/dist/location/types.d.ts +8 -2
  96. package/dist/location/types.d.ts.map +1 -1
  97. package/dist/money/MoneyField.d.ts +28 -0
  98. package/dist/money/MoneyField.d.ts.map +1 -0
  99. package/dist/money/MoneyField.js +269 -0
  100. package/dist/money/MoneyField.js.map +1 -0
  101. package/dist/money/index.d.ts +3 -0
  102. package/dist/money/index.d.ts.map +1 -0
  103. package/dist/money/index.js +3 -0
  104. package/dist/money/money.d.ts +27 -0
  105. package/dist/money/money.d.ts.map +1 -0
  106. package/dist/money/money.js +56 -0
  107. package/dist/money/money.js.map +1 -0
  108. package/dist/region/RegionField.js +1 -1
  109. package/dist/shared/builtIn.d.ts +3 -1
  110. package/dist/shared/builtIn.d.ts.map +1 -1
  111. package/dist/shared/builtIn.js +18 -8
  112. package/dist/shared/builtIn.js.map +1 -1
  113. package/dist/shared/dates.js +2 -2
  114. package/dist/shared/dates.js.map +1 -1
  115. package/dist/shared/flow.d.ts.map +1 -1
  116. package/dist/shared/flow.js +1 -0
  117. package/dist/shared/flow.js.map +1 -1
  118. package/dist/shared/part.d.ts +26 -0
  119. package/dist/shared/part.d.ts.map +1 -0
  120. package/dist/shared/part.js +64 -0
  121. package/dist/shared/part.js.map +1 -0
  122. package/dist/tel/TelField.css +25 -0
  123. package/dist/tel/TelField.d.ts +14 -2
  124. package/dist/tel/TelField.d.ts.map +1 -1
  125. package/dist/tel/TelField.js +246 -145
  126. package/dist/tel/TelField.js.map +1 -1
  127. package/dist/tel/TelField.module.js +12 -0
  128. package/dist/tel/TelField.module.js.map +1 -0
  129. package/dist/tel/index.d.ts +1 -1
  130. package/dist/tel/index.d.ts.map +1 -1
  131. package/dist/tel/index.js +2 -2
  132. package/dist/tel/phone.d.ts +28 -0
  133. package/dist/tel/phone.d.ts.map +1 -1
  134. package/dist/tel/phone.js +48 -1
  135. package/dist/tel/phone.js.map +1 -1
  136. package/dist/url/UrlField.d.ts.map +1 -1
  137. package/dist/url/UrlField.js +1 -1
  138. package/dist/url/UrlField.js.map +1 -1
  139. package/package.json +24 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range.js","names":["DateRangeOpenBound","DateRangeFieldValue","start","end","allDay","timeZone","DateRangeSettings","time","open","chooseTimeZone","DAY","isBound","value","undefined","isDateRangeValue","Array","isArray","getDateRangeSettings","settings","Record","isTimeZone","zone","Intl","DateTimeFormat","getTimeZones","zones","supportedValuesOf","filter","pad","length","String","padStart","dayIn","date","Date","parts","year","month","day","formatToParts","part","type","Number","find","entry","getBoundDay","bound","test","isNaN","getTime","getRangeDays","fieldTimeZone","first","last","FormattedDateRange","kind","text","formatDateRange","locale","bounds","map","days","every","toDate","local","resolvedOptions","format","hour","minute","timeZoneName","dates","some","from","to","formatRange"],"sources":["../../src/daterange/range.ts"],"sourcesContent":["import type { DateRangeOpenBound } from '@panneau/protocol/runtime';\n\n/**\n * A `daterange` value as a field holds it (decision 0053): days (`2026-11-04`) or date-times with\n * an offset, either `null` while it is empty, with `allDay` for an all-day range and the `timeZone`\n * the user chose.\n */\nexport interface DateRangeFieldValue {\n start: string | null;\n end: string | null;\n allDay?: boolean;\n timeZone?: string;\n}\n\n/** The settings of a `daterange` field, from its definition. */\nexport interface DateRangeSettings {\n /** The bounds are date-times. */\n time: boolean;\n /** The field has an \"All day\" switch. */\n allDay: boolean;\n /** The bounds that can be `null`. */\n open: DateRangeOpenBound | null;\n /** The time zone the date-times are shown and typed in. */\n timeZone: string | null;\n /** The field has a menu of time zones, kept in the value. */\n chooseTimeZone: boolean;\n}\n\nconst DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isBound = (value: unknown): value is string | null =>\n value === null || value === undefined || typeof value === 'string';\n\n/** Whether a value is a `daterange` value: an object with a start and an end. */\nexport function isDateRangeValue(value: unknown): value is DateRangeFieldValue {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n ('start' in value || 'end' in value) &&\n isBound((value as { start?: unknown }).start) &&\n isBound((value as { end?: unknown }).end)\n );\n}\n\n/** The settings of a `daterange` field (`settings.time`, `allDay`, `open`, `timeZone`…). */\nexport function getDateRangeSettings(\n settings: Record<string, unknown> | undefined,\n): DateRangeSettings {\n const open = settings?.open;\n const timeZone = settings?.timeZone;\n return {\n time: settings?.time === true,\n allDay: settings?.allDay === true && settings.time === true,\n open: open === 'start' || open === 'end' || open === 'both' ? open : null,\n timeZone: typeof timeZone === 'string' && isTimeZone(timeZone) ? timeZone : null,\n chooseTimeZone: settings?.chooseTimeZone === true && settings.time === true,\n };\n}\n\n/** Whether a string is an IANA time zone that the browser knows (`America/Toronto`). */\nexport function isTimeZone(zone: string): boolean {\n try {\n new Intl.DateTimeFormat('en', { timeZone: zone });\n return true;\n } catch {\n return false;\n }\n}\n\n/** The time zones the browser knows, `UTC` first. */\nexport function getTimeZones(): string[] {\n const zones =\n typeof Intl.supportedValuesOf === 'function' ? Intl.supportedValuesOf('timeZone') : [];\n return ['UTC', ...zones.filter((zone) => zone !== 'UTC')];\n}\n\nfunction pad(value: number, length = 2): string {\n return String(value).padStart(length, '0');\n}\n\n/** The day of a date-time in a time zone (the browser's without one), as `2026-11-04`. */\nfunction dayIn(date: Date, timeZone: string | undefined): string {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n }).formatToParts(date);\n const part = (type: string) => Number(parts.find((entry) => entry.type === type)?.value);\n return `${pad(part('year'), 4)}-${pad(part('month'))}-${pad(part('day'))}`;\n}\n\n/** The day of a bound: a day as it is, a date-time in the time zone. `null` when invalid. */\nexport function getBoundDay(bound: string | null | undefined, timeZone?: string): string | null {\n if (bound === null || bound === undefined || bound === '') {\n return null;\n }\n if (DAY.test(bound)) {\n return bound;\n }\n const date = new Date(bound);\n return Number.isNaN(date.getTime()) ? null : dayIn(date, timeZone);\n}\n\n/**\n * The first and the last day of a range, in its time zone: the value's, the field's, or the\n * browser's. A day bound is that day.\n */\nexport function getRangeDays(\n value: unknown,\n fieldTimeZone?: string | null,\n): { first: string | null; last: string | null } {\n if (!isDateRangeValue(value)) {\n return { first: null, last: null };\n }\n const zone = value.timeZone ?? fieldTimeZone ?? undefined;\n return { first: getBoundDay(value.start, zone), last: getBoundDay(value.end, zone) };\n}\n\n/** A range in a locale: both bounds, or one of them for a range open at the other end. */\nexport type FormattedDateRange =\n | { kind: 'range'; text: string }\n | { kind: 'from'; text: string }\n | { kind: 'until'; text: string };\n\n/**\n * A range in the format of a locale, with `Intl.DateTimeFormat.formatRange`: `Nov 4 – 6, 2026`,\n * `Nov 4, 2026, 7:30 – 9:00 PM`. Date-times show in the zone of the value, or the field's, with\n * the zone's short name when it isn't the browser's. `null` for an empty or invalid value.\n */\nexport function formatDateRange(\n value: unknown,\n locale: string,\n fieldTimeZone?: string | null,\n): FormattedDateRange | null {\n if (!isDateRangeValue(value)) {\n return null;\n }\n const bounds = [value.start, value.end].map((bound) =>\n bound === null || bound === undefined || bound === '' ? null : bound,\n );\n const days = bounds.every((bound) => bound === null || DAY.test(bound));\n const toDate = (bound: string) => new Date(DAY.test(bound) ? `${bound}T00:00:00Z` : bound);\n const local = Intl.DateTimeFormat().resolvedOptions().timeZone;\n const zone = value.timeZone ?? fieldTimeZone ?? undefined;\n const format = new Intl.DateTimeFormat(locale, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n ...(days\n ? { timeZone: 'UTC' }\n : {\n hour: 'numeric',\n minute: '2-digit',\n ...(zone !== undefined ? { timeZone: zone } : {}),\n ...(zone !== undefined && zone !== local ? { timeZoneName: 'short' } : {}),\n }),\n });\n const dates = bounds.map((bound) => (bound !== null ? toDate(bound) : null));\n if (dates.some((date) => date !== null && Number.isNaN(date.getTime()))) {\n return null;\n }\n const [from, to] = dates;\n if (from !== null && from !== undefined && to !== null && to !== undefined) {\n try {\n return { kind: 'range', text: format.formatRange(from, to) };\n } catch {\n // An end before its start, which some engines refuse to format as a range.\n return { kind: 'range', text: `${format.format(from)} – ${format.format(to)}` };\n }\n }\n if (from !== null && from !== undefined) {\n return { kind: 'from', text: format.format(from) };\n }\n if (to !== null && to !== undefined) {\n return { kind: 'until', text: format.format(to) };\n }\n return null;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- /* Hours, minutes and seconds side by side. */
1
+ /* The parts of the duration side by side: days, hours, minutes and seconds. */
2
2
  .panneau-duration-field-parts {
3
3
  display: flex;
4
4
  flex-wrap: wrap;
@@ -1,26 +1,67 @@
1
+ import type { Field } from '@panneau/protocol';
2
+ import { type DurationUnit } from '@panneau/protocol/runtime';
1
3
  import type { FieldEntryProps, FieldProps } from '../shared/types.js';
2
- export interface DurationFieldProps extends FieldProps<number> {
3
- /** `seconds` (the default) shows hours, minutes and seconds; `minutes` hides the seconds. */
4
- precision?: 'seconds' | 'minutes';
4
+ export type { DurationUnit };
5
+ /**
6
+ * The unit of a duration's value and the parts that show it (decision 0056). Every unit is
7
+ * `seconds`, `minutes`, `hours` or `days`, and a day is 24 hours.
8
+ */
9
+ export interface DurationUnits {
10
+ /** The unit of the value, a whole number: `seconds` by default. */
11
+ unit?: DurationUnit;
12
+ /** The smallest part shown. Defaults to `unit`; a precision finer than `unit` is raised to it. */
13
+ precision?: DurationUnit;
14
+ /**
15
+ * The largest part shown, which has no upper bound. Defaults to the larger of `hours` and
16
+ * `precision`; a largest unit smaller than `precision` is raised to it.
17
+ */
18
+ largestUnit?: DurationUnit;
19
+ }
20
+ /** The parts of a duration, from its largest unit to its precision. */
21
+ export type DurationParts = Partial<Record<DurationUnit, number>>;
22
+ /** Options of `formatDuration()`. */
23
+ export interface FormatDurationOptions extends DurationUnits {
24
+ /** The abbreviations of the units: `d`, `h`, `min` and `s` by default. */
25
+ labels?: Partial<Record<DurationUnit, string>>;
5
26
  }
6
- /** Hours, minutes and seconds of a number of seconds. */
27
+ /** Whether a value is a unit of a duration: `seconds`, `minutes`, `hours` or `days`. */
28
+ export declare function isDurationUnit(value: unknown): value is DurationUnit;
29
+ /**
30
+ * The units of a duration with their defaults: `seconds` for the value, a precision of the unit
31
+ * of the value, and a largest unit of `hours` or the precision, whichever is larger.
32
+ */
33
+ export declare function resolveDurationUnits({ unit, precision, largestUnit, }?: DurationUnits): Required<DurationUnits>;
34
+ /** The units of a `duration` field, from its settings `unit`, `precision` and `largestUnit`. */
35
+ export declare function getDurationUnits(field: Field): DurationUnits;
36
+ /**
37
+ * The parts of a duration, from its largest unit to its precision: hours, minutes and seconds of
38
+ * a number of seconds by default. The largest part takes what the larger units would have held
39
+ * (`48` hours for 2 days), and what is finer than the precision is dropped.
40
+ */
7
41
  export declare function splitDuration(seconds: number): {
8
42
  hours: number;
9
43
  minutes: number;
10
44
  seconds: number;
11
45
  };
46
+ /** The parts of a duration in `units.unit`, from `units.largestUnit` to `units.precision`. */
47
+ export declare function splitDuration(value: number, units: DurationUnits): DurationParts;
48
+ /**
49
+ * A duration with its units, without the empty ones: `1 h 5 min 30 s`, `3 min`, `2 d 4 h`. The
50
+ * value is in `unit` (seconds by default), and is rounded to the precision.
51
+ */
52
+ export declare function formatDuration(value: number, options?: FormatDurationOptions): string;
53
+ /** Props of `DurationField`: those of every field, and the units of the duration. */
54
+ export interface DurationFieldProps extends FieldProps<number>, DurationUnits {
55
+ }
12
56
  /**
13
- * A duration with its units, without the empty ones: `1 h 5 min 30 s`, `3 min`. With a precision
14
- * of minutes, the seconds are rounded away.
57
+ * A duration, a whole number of `unit` (seconds by default), typed in parts from `largestUnit`
58
+ * to `precision`: hours, minutes and seconds by default. The largest part has no upper bound.
59
+ * It is `null` while every part is empty.
15
60
  */
16
- export declare function formatDuration(seconds: number, { precision }?: {
17
- precision?: 'seconds' | 'minutes';
18
- }): string;
61
+ export declare function DurationField({ value, onChange, onBlur, unit: unitProp, precision: precisionProp, largestUnit: largestUnitProp, label, description, errors, disabled, readOnly, required, name, className, }: DurationFieldProps): import("react").JSX.Element;
19
62
  /**
20
- * A duration, in seconds, typed in hours, minutes and seconds. It is `null` while every part is
21
- * empty.
63
+ * The `duration` type: a whole number of `settings.unit`, with settings `precision` and
64
+ * `largestUnit` for the parts its input shows.
22
65
  */
23
- export declare function DurationField({ value, onChange, onBlur, precision, label, description, errors, disabled, readOnly, required, name, className, }: DurationFieldProps): import("react").JSX.Element;
24
- /** The `duration` type: a number of seconds, with setting `precision` (`seconds` or `minutes`). */
25
66
  export declare function DurationFieldEntry(props: FieldEntryProps): import("react").JSX.Element;
26
67
  //# sourceMappingURL=DurationField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DurationField.d.ts","sourceRoot":"","sources":["../../src/duration/DurationField.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGnE,MAAM,WAAW,kBAAmB,SAAQ,UAAU,CAAC,MAAM,CAAC;IAC1D,6FAA6F;IAC7F,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACrC;AAED,yDAAyD;AACzD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB,CAOA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,OAAO,EAAE,MAAM,EACf,EAAE,SAAqB,EAAE,GAAE;IAAE,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAAO,GACtE,MAAM,CAQR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAqB,EACrB,KAAK,EACL,WAAW,EACX,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,SAAS,GACZ,EAAE,kBAAkB,+BAyCpB;AAED,mGAAmG;AACnG,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,+BAWxD"}
1
+ {"version":3,"file":"DurationField.d.ts","sourceRoot":"","sources":["../../src/duration/DurationField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAK9E,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGnE,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B,mEAAmE;IACnE,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,kGAAkG;IAClG,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED,uEAAuE;AACvE,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;AAElE,qCAAqC;AACrC,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IACxD,0EAA0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;CAClD;AAmBD,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,IAAgB,EAChB,SAAS,EACT,WAAW,GACd,GAAE,aAAkB,GAAG,QAAQ,CAAC,aAAa,CAAC,CAG9C;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,aAAa,CAS5D;AAOD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AACpG,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,aAAa,CAAC;AAalF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAUzF;AAED,qFAAqF;AACrF,MAAM,WAAW,kBAAmB,SAAQ,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa;CAAG;AAEhF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,eAAe,EAC5B,KAAK,EACL,WAAW,EACX,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,SAAS,GACZ,EAAE,kBAAkB,+BA4DpB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,+BAUxD"}
@@ -1,44 +1,96 @@
1
- import { getFieldProps, getSetting, isNumber, isString } from "../shared/entry.js";
1
+ import { getFieldProps, getSetting, isNumber } from "../shared/entry.js";
2
2
  import { messages } from "../shared/messages.js";
3
3
  import DurationField_module_default from "./DurationField.module.js";
4
4
  import { c } from "react/compiler-runtime";
5
+ import { DURATION_UNITS } from "@panneau/protocol/runtime";
5
6
  import { Fieldset, NumberInput, usePanneauIntl } from "@panneau/ui";
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { jsx } from "react/jsx-runtime";
7
8
  //#region src/duration/DurationField.tsx
8
- /** Hours, minutes and seconds of a number of seconds. */
9
- function splitDuration(seconds) {
10
- const total = Math.max(0, Math.round(seconds));
9
+ var SECONDS = {
10
+ seconds: 1,
11
+ minutes: 60,
12
+ hours: 3600,
13
+ days: 86400
14
+ };
15
+ var LABELS = {
16
+ days: "d",
17
+ hours: "h",
18
+ minutes: "min",
19
+ seconds: "s"
20
+ };
21
+ var rank = (unit) => DURATION_UNITS.indexOf(unit);
22
+ var larger = (a, b) => rank(a) >= rank(b) ? a : b;
23
+ /** Whether a value is a unit of a duration: `seconds`, `minutes`, `hours` or `days`. */
24
+ function isDurationUnit(value) {
25
+ return typeof value === "string" && DURATION_UNITS.includes(value);
26
+ }
27
+ /**
28
+ * The units of a duration with their defaults: `seconds` for the value, a precision of the unit
29
+ * of the value, and a largest unit of `hours` or the precision, whichever is larger.
30
+ */
31
+ function resolveDurationUnits({ unit = "seconds", precision, largestUnit } = {}) {
32
+ const smallest = larger(precision ?? unit, unit);
33
+ return {
34
+ unit,
35
+ precision: smallest,
36
+ largestUnit: larger(largestUnit ?? "hours", smallest)
37
+ };
38
+ }
39
+ /** The units of a `duration` field, from its settings `unit`, `precision` and `largestUnit`. */
40
+ function getDurationUnits(field) {
41
+ const unit = getSetting(field, "unit", isDurationUnit);
42
+ const precision = getSetting(field, "precision", isDurationUnit);
43
+ const largestUnit = getSetting(field, "largestUnit", isDurationUnit);
11
44
  return {
12
- hours: Math.floor(total / 3600),
13
- minutes: Math.floor(total % 3600 / 60),
14
- seconds: total % 60
45
+ ...unit !== void 0 ? { unit } : {},
46
+ ...precision !== void 0 ? { precision } : {},
47
+ ...largestUnit !== void 0 ? { largestUnit } : {}
15
48
  };
16
49
  }
50
+ /** The units from the largest to the smallest, both included. */
51
+ function unitsBetween(largest, smallest) {
52
+ return DURATION_UNITS.slice(rank(smallest), rank(largest) + 1).reverse();
53
+ }
54
+ function splitDuration(value, units = {}) {
55
+ const { unit, precision, largestUnit } = resolveDurationUnits(units);
56
+ let rest = Math.max(0, Math.round(value)) * SECONDS[unit];
57
+ const parts = {};
58
+ for (const part of unitsBetween(largestUnit, precision)) {
59
+ const count = Math.floor(rest / SECONDS[part]);
60
+ parts[part] = count;
61
+ rest -= count * SECONDS[part];
62
+ }
63
+ return parts;
64
+ }
17
65
  /**
18
- * A duration with its units, without the empty ones: `1 h 5 min 30 s`, `3 min`. With a precision
19
- * of minutes, the seconds are rounded away.
66
+ * A duration with its units, without the empty ones: `1 h 5 min 30 s`, `3 min`, `2 d 4 h`. The
67
+ * value is in `unit` (seconds by default), and is rounded to the precision.
20
68
  */
21
- function formatDuration(seconds, { precision = "seconds" } = {}) {
22
- const parts = splitDuration(precision === "minutes" ? Math.round(seconds / 60) * 60 : seconds);
23
- const shown = [
24
- parts.hours > 0 ? `${parts.hours} h` : null,
25
- parts.minutes > 0 ? `${parts.minutes} min` : null,
26
- precision === "seconds" && parts.seconds > 0 ? `${parts.seconds} s` : null
27
- ].filter((part) => part !== null);
28
- return shown.length > 0 ? shown.join(" ") : precision === "minutes" ? "0 min" : "0 s";
69
+ function formatDuration(value, options = {}) {
70
+ const units = resolveDurationUnits(options);
71
+ const labels = {
72
+ ...LABELS,
73
+ ...options.labels
74
+ };
75
+ const step = SECONDS[units.precision] / SECONDS[units.unit];
76
+ const parts = splitDuration(Math.round(value / step) * step, units);
77
+ const shown = unitsBetween(units.largestUnit, units.precision).flatMap((part) => {
78
+ const count = parts[part] ?? 0;
79
+ return count > 0 ? [`${String(count)} ${labels[part]}`] : [];
80
+ });
81
+ return shown.length > 0 ? shown.join(" ") : `0 ${labels[units.precision]}`;
29
82
  }
30
83
  /**
31
- * A duration, in seconds, typed in hours, minutes and seconds. It is `null` while every part is
32
- * empty.
84
+ * A duration, a whole number of `unit` (seconds by default), typed in parts from `largestUnit`
85
+ * to `precision`: hours, minutes and seconds by default. The largest part has no upper bound.
86
+ * It is `null` while every part is empty.
33
87
  */
34
88
  function DurationField(t0) {
35
- const $ = c(38);
36
- const { value, onChange, onBlur, precision: t1, label, description, errors, disabled, readOnly, required, name, className } = t0;
37
- const precision = t1 === void 0 ? "seconds" : t1;
89
+ const $ = c(36);
90
+ const { value, onChange, onBlur, unit: unitProp, precision: precisionProp, largestUnit: largestUnitProp, label, description, errors, disabled, readOnly, required, name, className } = t0;
38
91
  const intl = usePanneauIntl();
39
92
  let T0;
40
- let t10;
41
- let t11;
93
+ let t1;
42
94
  let t2;
43
95
  let t3;
44
96
  let t4;
@@ -46,169 +98,163 @@ function DurationField(t0) {
46
98
  let t6;
47
99
  let t7;
48
100
  let t8;
49
- let t9;
50
- if ($[0] !== className || $[1] !== description || $[2] !== disabled || $[3] !== errors || $[4] !== intl || $[5] !== label || $[6] !== name || $[7] !== onBlur || $[8] !== onChange || $[9] !== precision || $[10] !== readOnly || $[11] !== required || $[12] !== value) {
51
- const parts = value !== null && value !== void 0 ? splitDuration(value) : null;
101
+ if ($[0] !== className || $[1] !== description || $[2] !== disabled || $[3] !== errors || $[4] !== intl || $[5] !== label || $[6] !== largestUnitProp || $[7] !== name || $[8] !== onBlur || $[9] !== onChange || $[10] !== precisionProp || $[11] !== readOnly || $[12] !== required || $[13] !== unitProp || $[14] !== value) {
102
+ const { unit, precision, largestUnit } = resolveDurationUnits({
103
+ ...unitProp !== void 0 ? { unit: unitProp } : {},
104
+ ...precisionProp !== void 0 ? { precision: precisionProp } : {},
105
+ ...largestUnitProp !== void 0 ? { largestUnit: largestUnitProp } : {}
106
+ });
107
+ const all = unitsBetween(largestUnit, unit);
108
+ const parts = value !== null && value !== void 0 ? splitDuration(value, {
109
+ unit,
110
+ precision: unit,
111
+ largestUnit
112
+ }) : null;
52
113
  const change = (part, next) => {
53
114
  const updated = {
54
- ...parts ?? {
55
- hours: 0,
56
- minutes: 0,
57
- seconds: 0
58
- },
115
+ ...parts,
59
116
  [part]: next ?? 0
60
117
  };
61
- const total = updated.hours * 3600 + updated.minutes * 60 + updated.seconds;
118
+ const total = all.reduce((sum, current) => sum + (updated[current] ?? 0) * SECONDS[current] / SECONDS[unit], 0);
62
119
  onChange?.(total === 0 && next === null ? null : total);
63
120
  };
64
- const input = (part_0) => /* @__PURE__ */ jsx(NumberInput, {
65
- "aria-label": intl.formatMessage(messages[part_0]),
66
- value: parts?.[part_0] ?? null,
67
- onChange: (next_0) => change(part_0, next_0),
68
- onBlur: () => onBlur?.(),
69
- minValue: 0,
70
- ...part_0 !== "hours" ? { maxValue: 59 } : {},
71
- formatOptions: { maximumFractionDigits: 0 },
72
- suffix: intl.formatMessage(messages[`${part_0}Unit`]),
73
- isDisabled: disabled,
74
- isReadOnly: readOnly,
75
- ...name !== void 0 ? { name: `${name}[${part_0}]` } : {},
76
- className: DurationField_module_default.part
77
- });
121
+ const input = (part_0) => {
122
+ const above = DURATION_UNITS[rank(part_0) + 1];
123
+ return /* @__PURE__ */ jsx(NumberInput, {
124
+ "aria-label": intl.formatMessage(messages[part_0]),
125
+ value: parts?.[part_0] ?? null,
126
+ onChange: (next_0) => change(part_0, next_0),
127
+ onBlur: () => onBlur?.(),
128
+ minValue: 0,
129
+ ...part_0 !== largestUnit && above !== void 0 ? { maxValue: SECONDS[above] / SECONDS[part_0] - 1 } : {},
130
+ formatOptions: { maximumFractionDigits: 0 },
131
+ suffix: intl.formatMessage(messages[`${part_0}Unit`]),
132
+ isDisabled: disabled,
133
+ isReadOnly: readOnly,
134
+ ...name !== void 0 ? { name: `${name}[${part_0}]` } : {},
135
+ className: DurationField_module_default.part
136
+ }, part_0);
137
+ };
78
138
  T0 = Fieldset;
79
- t6 = label;
80
- t7 = description;
81
- t8 = errors;
82
- t9 = required;
83
- t10 = disabled;
84
- t11 = className;
85
- t2 = DurationField_module_default.parts;
86
- t3 = input("hours");
87
- t4 = input("minutes");
88
- t5 = precision === "seconds" ? input("seconds") : null;
139
+ t3 = label;
140
+ t4 = description;
141
+ t5 = errors;
142
+ t6 = required;
143
+ t7 = disabled;
144
+ t8 = className;
145
+ t1 = DurationField_module_default.parts;
146
+ t2 = unitsBetween(largestUnit, precision).map((part_1) => input(part_1));
89
147
  $[0] = className;
90
148
  $[1] = description;
91
149
  $[2] = disabled;
92
150
  $[3] = errors;
93
151
  $[4] = intl;
94
152
  $[5] = label;
95
- $[6] = name;
96
- $[7] = onBlur;
97
- $[8] = onChange;
98
- $[9] = precision;
99
- $[10] = readOnly;
100
- $[11] = required;
101
- $[12] = value;
102
- $[13] = T0;
103
- $[14] = t10;
104
- $[15] = t11;
105
- $[16] = t2;
106
- $[17] = t3;
107
- $[18] = t4;
108
- $[19] = t5;
109
- $[20] = t6;
110
- $[21] = t7;
111
- $[22] = t8;
112
- $[23] = t9;
153
+ $[6] = largestUnitProp;
154
+ $[7] = name;
155
+ $[8] = onBlur;
156
+ $[9] = onChange;
157
+ $[10] = precisionProp;
158
+ $[11] = readOnly;
159
+ $[12] = required;
160
+ $[13] = unitProp;
161
+ $[14] = value;
162
+ $[15] = T0;
163
+ $[16] = t1;
164
+ $[17] = t2;
165
+ $[18] = t3;
166
+ $[19] = t4;
167
+ $[20] = t5;
168
+ $[21] = t6;
169
+ $[22] = t7;
170
+ $[23] = t8;
113
171
  } else {
114
- T0 = $[13];
115
- t10 = $[14];
116
- t11 = $[15];
117
- t2 = $[16];
118
- t3 = $[17];
119
- t4 = $[18];
120
- t5 = $[19];
121
- t6 = $[20];
122
- t7 = $[21];
123
- t8 = $[22];
124
- t9 = $[23];
172
+ T0 = $[15];
173
+ t1 = $[16];
174
+ t2 = $[17];
175
+ t3 = $[18];
176
+ t4 = $[19];
177
+ t5 = $[20];
178
+ t6 = $[21];
179
+ t7 = $[22];
180
+ t8 = $[23];
125
181
  }
126
- let t12;
127
- if ($[24] !== t2 || $[25] !== t3 || $[26] !== t4 || $[27] !== t5) {
128
- t12 = /* @__PURE__ */ jsxs("div", {
129
- className: t2,
130
- children: [
131
- t3,
132
- t4,
133
- t5
134
- ]
182
+ let t9;
183
+ if ($[24] !== t1 || $[25] !== t2) {
184
+ t9 = /* @__PURE__ */ jsx("div", {
185
+ className: t1,
186
+ children: t2
135
187
  });
136
- $[24] = t2;
137
- $[25] = t3;
138
- $[26] = t4;
139
- $[27] = t5;
140
- $[28] = t12;
141
- } else t12 = $[28];
142
- let t13;
143
- if ($[29] !== T0 || $[30] !== t10 || $[31] !== t11 || $[32] !== t12 || $[33] !== t6 || $[34] !== t7 || $[35] !== t8 || $[36] !== t9) {
144
- t13 = /* @__PURE__ */ jsx(T0, {
145
- legend: t6,
146
- description: t7,
147
- errors: t8,
148
- isRequired: t9,
149
- isDisabled: t10,
150
- className: t11,
151
- children: t12
188
+ $[24] = t1;
189
+ $[25] = t2;
190
+ $[26] = t9;
191
+ } else t9 = $[26];
192
+ let t10;
193
+ if ($[27] !== T0 || $[28] !== t3 || $[29] !== t4 || $[30] !== t5 || $[31] !== t6 || $[32] !== t7 || $[33] !== t8 || $[34] !== t9) {
194
+ t10 = /* @__PURE__ */ jsx(T0, {
195
+ legend: t3,
196
+ description: t4,
197
+ errors: t5,
198
+ isRequired: t6,
199
+ isDisabled: t7,
200
+ className: t8,
201
+ children: t9
152
202
  });
153
- $[29] = T0;
154
- $[30] = t10;
155
- $[31] = t11;
156
- $[32] = t12;
157
- $[33] = t6;
158
- $[34] = t7;
159
- $[35] = t8;
160
- $[36] = t9;
161
- $[37] = t13;
162
- } else t13 = $[37];
163
- return t13;
203
+ $[27] = T0;
204
+ $[28] = t3;
205
+ $[29] = t4;
206
+ $[30] = t5;
207
+ $[31] = t6;
208
+ $[32] = t7;
209
+ $[33] = t8;
210
+ $[34] = t9;
211
+ $[35] = t10;
212
+ } else t10 = $[35];
213
+ return t10;
164
214
  }
165
- /** The `duration` type: a number of seconds, with setting `precision` (`seconds` or `minutes`). */
215
+ /**
216
+ * The `duration` type: a whole number of `settings.unit`, with settings `precision` and
217
+ * `largestUnit` for the parts its input shows.
218
+ */
166
219
  function DurationFieldEntry(props) {
167
- const $ = c(13);
220
+ const $ = c(11);
168
221
  let t0;
169
- if ($[0] !== props.field) {
170
- t0 = getSetting(props.field, "precision", isString);
171
- $[0] = props.field;
222
+ if ($[0] !== props) {
223
+ t0 = getFieldProps(props);
224
+ $[0] = props;
172
225
  $[1] = t0;
173
226
  } else t0 = $[1];
174
- const precision = t0;
175
- let t1;
176
- if ($[2] !== props) {
177
- t1 = getFieldProps(props);
178
- $[2] = props;
179
- $[3] = t1;
180
- } else t1 = $[3];
181
227
  let fieldProps;
182
- if ($[4] !== t1) {
183
- const { placeholder: _placeholder, ...t2 } = t1;
184
- fieldProps = t2;
185
- $[4] = t1;
186
- $[5] = fieldProps;
187
- } else fieldProps = $[5];
228
+ if ($[2] !== t0) {
229
+ const { placeholder: _placeholder, ...t1 } = t0;
230
+ fieldProps = t1;
231
+ $[2] = t0;
232
+ $[3] = fieldProps;
233
+ } else fieldProps = $[3];
234
+ let t1;
235
+ if ($[4] !== props.field) {
236
+ t1 = getDurationUnits(props.field);
237
+ $[4] = props.field;
238
+ $[5] = t1;
239
+ } else t1 = $[5];
188
240
  const t2 = isNumber(props.value) ? props.value : null;
189
241
  let t3;
190
- if ($[6] !== precision) {
191
- t3 = precision === "minutes" || precision === "seconds" ? { precision } : {};
192
- $[6] = precision;
193
- $[7] = t3;
194
- } else t3 = $[7];
195
- let t4;
196
- if ($[8] !== fieldProps || $[9] !== props.onChange || $[10] !== t2 || $[11] !== t3) {
197
- t4 = /* @__PURE__ */ jsx(DurationField, {
242
+ if ($[6] !== fieldProps || $[7] !== props.onChange || $[8] !== t1 || $[9] !== t2) {
243
+ t3 = /* @__PURE__ */ jsx(DurationField, {
198
244
  ...fieldProps,
245
+ ...t1,
199
246
  value: t2,
200
- onChange: props.onChange,
201
- ...t3
247
+ onChange: props.onChange
202
248
  });
203
- $[8] = fieldProps;
204
- $[9] = props.onChange;
205
- $[10] = t2;
206
- $[11] = t3;
207
- $[12] = t4;
208
- } else t4 = $[12];
209
- return t4;
249
+ $[6] = fieldProps;
250
+ $[7] = props.onChange;
251
+ $[8] = t1;
252
+ $[9] = t2;
253
+ $[10] = t3;
254
+ } else t3 = $[10];
255
+ return t3;
210
256
  }
211
257
  //#endregion
212
- export { DurationField, DurationFieldEntry, formatDuration, splitDuration };
258
+ export { DurationField, DurationFieldEntry, formatDuration, getDurationUnits, isDurationUnit, resolveDurationUnits, splitDuration };
213
259
 
214
260
  //# sourceMappingURL=DurationField.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DurationField.js","names":["Fieldset","NumberInput","usePanneauIntl","getFieldProps","getSetting","isNumber","isString","messages","FieldEntryProps","FieldProps","styles","DurationFieldProps","precision","splitDuration","seconds","hours","minutes","total","Math","max","round","floor","formatDuration","parts","shown","filter","part","length","join","DurationField","t0","$","_c","value","onChange","onBlur","t1","label","description","errors","disabled","readOnly","required","name","className","undefined","intl","T0","t10","t11","t2","t3","t4","t5","t6","t7","t8","t9","change","next","updated","input","part_0","formatMessage","next_0","maxValue","maximumFractionDigits","t12","t13","DurationFieldEntry","props","field","fieldProps","placeholder","_placeholder","_c","styles"],"sources":["../../src/duration/DurationField.tsx"],"sourcesContent":["import { Fieldset, NumberInput, usePanneauIntl } from '@panneau/ui';\n\nimport { getFieldProps, getSetting, isNumber, isString } from '../shared/entry';\nimport { messages } from '../shared/messages';\nimport type { FieldEntryProps, FieldProps } from '../shared/types';\nimport styles from './DurationField.module.css';\n\nexport interface DurationFieldProps extends FieldProps<number> {\n /** `seconds` (the default) shows hours, minutes and seconds; `minutes` hides the seconds. */\n precision?: 'seconds' | 'minutes';\n}\n\n/** Hours, minutes and seconds of a number of seconds. */\nexport function splitDuration(seconds: number): {\n hours: number;\n minutes: number;\n seconds: number;\n} {\n const total = Math.max(0, Math.round(seconds));\n return {\n hours: Math.floor(total / 3600),\n minutes: Math.floor((total % 3600) / 60),\n seconds: total % 60,\n };\n}\n\n/**\n * A duration with its units, without the empty ones: `1 h 5 min 30 s`, `3 min`. With a precision\n * of minutes, the seconds are rounded away.\n */\nexport function formatDuration(\n seconds: number,\n { precision = 'seconds' }: { precision?: 'seconds' | 'minutes' } = {},\n): string {\n const parts = splitDuration(precision === 'minutes' ? Math.round(seconds / 60) * 60 : seconds);\n const shown = [\n parts.hours > 0 ? `${parts.hours} h` : null,\n parts.minutes > 0 ? `${parts.minutes} min` : null,\n precision === 'seconds' && parts.seconds > 0 ? `${parts.seconds} s` : null,\n ].filter((part) => part !== null);\n return shown.length > 0 ? shown.join(' ') : precision === 'minutes' ? '0 min' : '0 s';\n}\n\n/**\n * A duration, in seconds, typed in hours, minutes and seconds. It is `null` while every part is\n * empty.\n */\nexport function DurationField({\n value,\n onChange,\n onBlur,\n precision = 'seconds',\n label,\n description,\n errors,\n disabled,\n readOnly,\n required,\n name,\n className,\n}: DurationFieldProps) {\n const intl = usePanneauIntl();\n const parts = value !== null && value !== undefined ? splitDuration(value) : null;\n const change = (part: 'hours' | 'minutes' | 'seconds', next: number | null) => {\n const updated = { ...(parts ?? { hours: 0, minutes: 0, seconds: 0 }), [part]: next ?? 0 };\n const total = updated.hours * 3600 + updated.minutes * 60 + updated.seconds;\n // Clearing the last part that had a value empties the field\n onChange?.(total === 0 && next === null ? null : total);\n };\n const input = (part: 'hours' | 'minutes' | 'seconds') => (\n <NumberInput\n aria-label={intl.formatMessage(messages[part])}\n value={parts?.[part] ?? null}\n onChange={(next) => change(part, next)}\n onBlur={() => onBlur?.()}\n minValue={0}\n {...(part !== 'hours' ? { maxValue: 59 } : {})}\n formatOptions={{ maximumFractionDigits: 0 }}\n suffix={intl.formatMessage(messages[`${part}Unit`])}\n isDisabled={disabled}\n isReadOnly={readOnly}\n {...(name !== undefined ? { name: `${name}[${part}]` } : {})}\n className={styles.part}\n />\n );\n return (\n <Fieldset\n legend={label}\n description={description}\n errors={errors}\n isRequired={required}\n isDisabled={disabled}\n className={className}\n >\n <div className={styles.parts}>\n {input('hours')}\n {input('minutes')}\n {precision === 'seconds' ? input('seconds') : null}\n </div>\n </Fieldset>\n );\n}\n\n/** The `duration` type: a number of seconds, with setting `precision` (`seconds` or `minutes`). */\nexport function DurationFieldEntry(props: FieldEntryProps) {\n const precision = getSetting(props.field, 'precision', isString);\n const { placeholder: _placeholder, ...fieldProps } = getFieldProps(props);\n return (\n <DurationField\n {...fieldProps}\n value={isNumber(props.value) ? props.value : null}\n onChange={props.onChange}\n {...(precision === 'minutes' || precision === 'seconds' ? { precision } : {})}\n />\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DurationField.js","names":["Field","DURATION_UNITS","DurationUnit","Fieldset","NumberInput","usePanneauIntl","getFieldProps","getSetting","isNumber","messages","FieldEntryProps","FieldProps","styles","DurationUnits","unit","precision","largestUnit","DurationParts","Partial","Record","FormatDurationOptions","labels","SECONDS","Readonly","seconds","minutes","hours","days","LABELS","rank","indexOf","larger","a","b","isDurationUnit","value","includes","resolveDurationUnits","Required","smallest","getDurationUnits","field","undefined","unitsBetween","largest","slice","reverse","splitDuration","units","rest","Math","max","round","parts","part","count","floor","formatDuration","options","step","shown","flatMap","String","length","join","DurationFieldProps","DurationField","t0","$","_c","onChange","onBlur","unitProp","precisionProp","largestUnitProp","label","description","errors","disabled","readOnly","required","name","className","intl","T0","t1","t2","t3","t4","t5","t6","t7","t8","all","change","next","updated","total","reduce","sum","current","input","part_0","above","formatMessage","next_0","maxValue","maximumFractionDigits","map","part_1","t9","t10","DurationFieldEntry","props","fieldProps","placeholder","_placeholder","_c","styles"],"sources":["../../src/duration/DurationField.tsx"],"sourcesContent":["import type { Field } from '@panneau/protocol';\nimport { DURATION_UNITS, type DurationUnit } from '@panneau/protocol/runtime';\nimport { Fieldset, NumberInput, usePanneauIntl } from '@panneau/ui';\n\nimport { getFieldProps, getSetting, isNumber } from '../shared/entry';\nimport { messages } from '../shared/messages';\nimport type { FieldEntryProps, FieldProps } from '../shared/types';\nimport styles from './DurationField.module.css';\n\nexport type { DurationUnit };\n\n/**\n * The unit of a duration's value and the parts that show it (decision 0056). Every unit is\n * `seconds`, `minutes`, `hours` or `days`, and a day is 24 hours.\n */\nexport interface DurationUnits {\n /** The unit of the value, a whole number: `seconds` by default. */\n unit?: DurationUnit;\n /** The smallest part shown. Defaults to `unit`; a precision finer than `unit` is raised to it. */\n precision?: DurationUnit;\n /**\n * The largest part shown, which has no upper bound. Defaults to the larger of `hours` and\n * `precision`; a largest unit smaller than `precision` is raised to it.\n */\n largestUnit?: DurationUnit;\n}\n\n/** The parts of a duration, from its largest unit to its precision. */\nexport type DurationParts = Partial<Record<DurationUnit, number>>;\n\n/** Options of `formatDuration()`. */\nexport interface FormatDurationOptions extends DurationUnits {\n /** The abbreviations of the units: `d`, `h`, `min` and `s` by default. */\n labels?: Partial<Record<DurationUnit, string>>;\n}\n\nconst SECONDS: Readonly<Record<DurationUnit, number>> = {\n seconds: 1,\n minutes: 60,\n hours: 3600,\n days: 86_400,\n};\n\nconst LABELS: Readonly<Record<DurationUnit, string>> = {\n days: 'd',\n hours: 'h',\n minutes: 'min',\n seconds: 's',\n};\n\nconst rank = (unit: DurationUnit) => DURATION_UNITS.indexOf(unit);\nconst larger = (a: DurationUnit, b: DurationUnit) => (rank(a) >= rank(b) ? a : b);\n\n/** Whether a value is a unit of a duration: `seconds`, `minutes`, `hours` or `days`. */\nexport function isDurationUnit(value: unknown): value is DurationUnit {\n return typeof value === 'string' && (DURATION_UNITS as readonly string[]).includes(value);\n}\n\n/**\n * The units of a duration with their defaults: `seconds` for the value, a precision of the unit\n * of the value, and a largest unit of `hours` or the precision, whichever is larger.\n */\nexport function resolveDurationUnits({\n unit = 'seconds',\n precision,\n largestUnit,\n}: DurationUnits = {}): Required<DurationUnits> {\n const smallest = larger(precision ?? unit, unit);\n return { unit, precision: smallest, largestUnit: larger(largestUnit ?? 'hours', smallest) };\n}\n\n/** The units of a `duration` field, from its settings `unit`, `precision` and `largestUnit`. */\nexport function getDurationUnits(field: Field): DurationUnits {\n const unit = getSetting(field, 'unit', isDurationUnit);\n const precision = getSetting(field, 'precision', isDurationUnit);\n const largestUnit = getSetting(field, 'largestUnit', isDurationUnit);\n return {\n ...(unit !== undefined ? { unit } : {}),\n ...(precision !== undefined ? { precision } : {}),\n ...(largestUnit !== undefined ? { largestUnit } : {}),\n };\n}\n\n/** The units from the largest to the smallest, both included. */\nfunction unitsBetween(largest: DurationUnit, smallest: DurationUnit): DurationUnit[] {\n return DURATION_UNITS.slice(rank(smallest), rank(largest) + 1).reverse();\n}\n\n/**\n * The parts of a duration, from its largest unit to its precision: hours, minutes and seconds of\n * a number of seconds by default. The largest part takes what the larger units would have held\n * (`48` hours for 2 days), and what is finer than the precision is dropped.\n */\nexport function splitDuration(seconds: number): { hours: number; minutes: number; seconds: number };\n/** The parts of a duration in `units.unit`, from `units.largestUnit` to `units.precision`. */\nexport function splitDuration(value: number, units: DurationUnits): DurationParts;\nexport function splitDuration(value: number, units: DurationUnits = {}): DurationParts {\n const { unit, precision, largestUnit } = resolveDurationUnits(units);\n let rest = Math.max(0, Math.round(value)) * SECONDS[unit];\n const parts: DurationParts = {};\n for (const part of unitsBetween(largestUnit, precision)) {\n const count = Math.floor(rest / SECONDS[part]);\n parts[part] = count;\n rest -= count * SECONDS[part];\n }\n return parts;\n}\n\n/**\n * A duration with its units, without the empty ones: `1 h 5 min 30 s`, `3 min`, `2 d 4 h`. The\n * value is in `unit` (seconds by default), and is rounded to the precision.\n */\nexport function formatDuration(value: number, options: FormatDurationOptions = {}): string {\n const units = resolveDurationUnits(options);\n const labels = { ...LABELS, ...options.labels };\n const step = SECONDS[units.precision] / SECONDS[units.unit];\n const parts = splitDuration(Math.round(value / step) * step, units);\n const shown = unitsBetween(units.largestUnit, units.precision).flatMap((part) => {\n const count = parts[part] ?? 0;\n return count > 0 ? [`${String(count)} ${labels[part]}`] : [];\n });\n return shown.length > 0 ? shown.join(' ') : `0 ${labels[units.precision]}`;\n}\n\n/** Props of `DurationField`: those of every field, and the units of the duration. */\nexport interface DurationFieldProps extends FieldProps<number>, DurationUnits {}\n\n/**\n * A duration, a whole number of `unit` (seconds by default), typed in parts from `largestUnit`\n * to `precision`: hours, minutes and seconds by default. The largest part has no upper bound.\n * It is `null` while every part is empty.\n */\nexport function DurationField({\n value,\n onChange,\n onBlur,\n unit: unitProp,\n precision: precisionProp,\n largestUnit: largestUnitProp,\n label,\n description,\n errors,\n disabled,\n readOnly,\n required,\n name,\n className,\n}: DurationFieldProps) {\n const intl = usePanneauIntl();\n const { unit, precision, largestUnit } = resolveDurationUnits({\n ...(unitProp !== undefined ? { unit: unitProp } : {}),\n ...(precisionProp !== undefined ? { precision: precisionProp } : {}),\n ...(largestUnitProp !== undefined ? { largestUnit: largestUnitProp } : {}),\n });\n // Every part down to the unit of the value: parts finer than the precision are kept as they\n // are when another part changes.\n const all = unitsBetween(largestUnit, unit);\n const parts =\n value !== null && value !== undefined\n ? splitDuration(value, { unit, precision: unit, largestUnit })\n : null;\n const change = (part: DurationUnit, next: number | null) => {\n const updated: DurationParts = { ...parts, [part]: next ?? 0 };\n const total = all.reduce(\n (sum, current) => sum + ((updated[current] ?? 0) * SECONDS[current]) / SECONDS[unit],\n 0,\n );\n // Clearing the last part that had a value empties the field\n onChange?.(total === 0 && next === null ? null : total);\n };\n const input = (part: DurationUnit) => {\n const above = DURATION_UNITS[rank(part) + 1];\n return (\n <NumberInput\n key={part}\n aria-label={intl.formatMessage(messages[part])}\n value={parts?.[part] ?? null}\n onChange={(next) => change(part, next)}\n onBlur={() => onBlur?.()}\n minValue={0}\n // The largest part has no upper bound: 90 minutes, or 48 hours without days\n {...(part !== largestUnit && above !== undefined\n ? { maxValue: SECONDS[above] / SECONDS[part] - 1 }\n : {})}\n formatOptions={{ maximumFractionDigits: 0 }}\n suffix={intl.formatMessage(messages[`${part}Unit`])}\n isDisabled={disabled}\n isReadOnly={readOnly}\n {...(name !== undefined ? { name: `${name}[${part}]` } : {})}\n className={styles.part}\n />\n );\n };\n return (\n <Fieldset\n legend={label}\n description={description}\n errors={errors}\n isRequired={required}\n isDisabled={disabled}\n className={className}\n >\n <div className={styles.parts}>\n {unitsBetween(largestUnit, precision).map((part) => input(part))}\n </div>\n </Fieldset>\n );\n}\n\n/**\n * The `duration` type: a whole number of `settings.unit`, with settings `precision` and\n * `largestUnit` for the parts its input shows.\n */\nexport function DurationFieldEntry(props: FieldEntryProps) {\n const { placeholder: _placeholder, ...fieldProps } = getFieldProps(props);\n return (\n <DurationField\n {...fieldProps}\n {...getDurationUnits(props.field)}\n value={isNumber(props.value) ? props.value : null}\n onChange={props.onChange}\n />\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DurationField.module.js","names":[],"sources":["../../src/duration/DurationField.module.css"],"sourcesContent":["/* Hours, minutes and seconds side by side. */\n.parts {\n display: flex;\n flex-wrap: wrap;\n gap: var(--panneau-space-2);\n}\n\n.part {\n flex: 0 1 7rem;\n min-width: 5rem;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"DurationField.module.js","names":[],"sources":["../../src/duration/DurationField.module.css"],"sourcesContent":["/* The parts of the duration side by side: days, hours, minutes and seconds. */\n.parts {\n display: flex;\n flex-wrap: wrap;\n gap: var(--panneau-space-2);\n}\n\n.part {\n flex: 0 1 7rem;\n min-width: 5rem;\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- export { DurationField, DurationFieldEntry, type DurationFieldProps, formatDuration, splitDuration, } from './DurationField.js';
1
+ export { DurationField, DurationFieldEntry, type DurationFieldProps, type DurationParts, type DurationUnit, type DurationUnits, type FormatDurationOptions, formatDuration, getDurationUnits, isDurationUnit, resolveDurationUnits, splitDuration, } from './DurationField.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duration/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,cAAc,EACd,aAAa,GAChB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duration/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,aAAa,GAChB,MAAM,iBAAiB,CAAC"}