@pathscale/ui 3.1.0 → 3.2.0

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 (60) hide show
  1. package/README.md +13 -2
  2. package/dist/components/accordion/Accordion.css +24 -1
  3. package/dist/components/alert/Alert.css +11 -1
  4. package/dist/components/button/Button.css +33 -6
  5. package/dist/components/calendar/Calendar.generated.d.ts +19 -2
  6. package/dist/components/calendar/Calendar.generated.js +30 -23
  7. package/dist/components/calendar/index.d.ts +1 -1
  8. package/dist/components/card/Card.generated.d.ts +17 -0
  9. package/dist/components/card/Card.generated.js +99 -45
  10. package/dist/components/card/Card.interactions.d.ts +70 -0
  11. package/dist/components/card/Card.interactions.js +19 -0
  12. package/dist/components/collapsible/Collapsible.css +17 -1
  13. package/dist/components/color-wheel-flower/ColorWheelFlower.generated.d.ts +8 -0
  14. package/dist/components/color-wheel-flower/ColorWheelFlower.generated.js +14 -1
  15. package/dist/components/color-wheel-flower/colorWheelFlowerContext.d.ts +9 -2
  16. package/dist/components/color-wheel-flower/colorWheelFlowerContext.js +2 -4
  17. package/dist/components/date-picker/DatePicker.generated.d.ts +3 -1
  18. package/dist/components/date-picker/DatePicker.generated.js +8 -5
  19. package/dist/components/date-range-picker/DateRangePicker.generated.d.ts +3 -1
  20. package/dist/components/date-range-picker/DateRangePicker.generated.js +9 -6
  21. package/dist/components/drawer/Drawer.generated.d.ts +24 -0
  22. package/dist/components/drawer/Drawer.generated.js +48 -18
  23. package/dist/components/icon/Icon.generated.d.ts +15 -0
  24. package/dist/components/icon/Icon.generated.js +11 -2
  25. package/dist/components/immersive-landing/ImmersiveLanding.css +1 -1
  26. package/dist/components/immersive-landing/components/CookieConsent.interactions.d.ts +24 -0
  27. package/dist/components/immersive-landing/components/CookieConsent.interactions.js +2 -0
  28. package/dist/components/immersive-landing/components/CookieConsent.js +52 -47
  29. package/dist/components/immersive-landing/components/FirefoxPWABanner.js +10 -10
  30. package/dist/components/immersive-landing/types.d.ts +15 -0
  31. package/dist/components/language-switcher/LanguageSwitcher.generated.js +1 -1
  32. package/dist/components/live-chat/LiveChatPanel.generated.js +2 -6
  33. package/dist/components/live-chat/LiveChatPanel.interactions.d.ts +21 -0
  34. package/dist/components/live-chat/LiveChatPanel.interactions.js +10 -0
  35. package/dist/components/popover/Popover.a11y.d.ts +47 -0
  36. package/dist/components/popover/Popover.a11y.js +19 -0
  37. package/dist/components/popover/Popover.generated.d.ts +26 -0
  38. package/dist/components/popover/Popover.generated.js +105 -35
  39. package/dist/components/range-calendar/RangeCalendar.generated.d.ts +3 -1
  40. package/dist/components/range-calendar/RangeCalendar.generated.js +4 -1
  41. package/dist/components/table/MobileListView.generated.js +1 -1
  42. package/dist/components/text/Text.generated.d.ts +26 -1
  43. package/dist/components/text/Text.generated.js +13 -8
  44. package/dist/components/text/index.d.ts +1 -1
  45. package/dist/components/theme-color-picker/ThemeColorPicker.generated.js +1 -1
  46. package/dist/hooks/date/date.names.d.ts +107 -0
  47. package/dist/hooks/date/date.names.js +74 -0
  48. package/dist/hooks/date/date.utils.d.ts +11 -1
  49. package/dist/hooks/date/date.utils.js +3 -6
  50. package/dist/hooks/date/index.d.ts +1 -0
  51. package/dist/hooks/date/index.js +1 -0
  52. package/dist/hooks/date/useCalendarState.d.ts +12 -5
  53. package/dist/hooks/date/useCalendarState.js +7 -19
  54. package/dist/hooks/form/createForm.d.ts +4 -4
  55. package/dist/hooks/form/createForm.js +7 -9
  56. package/dist/index.d.ts +3 -3
  57. package/dist/index.js +1 -1
  58. package/dist/layouts.manifest.json +4 -1
  59. package/dist/responsive-classes.txt +450 -0
  60. package/package.json +10 -5
@@ -1,4 +1,4 @@
1
- import { Portal, createComponent, insert, mergeProps, ref, spread, template } from "@solidjs/web";
1
+ import { Dynamic, Portal, createComponent, insert, memo, mergeProps, ref, spread, template } from "@solidjs/web";
2
2
  import { defineComponent } from "solid-layouts/solid-2/application-boundary";
3
3
  import "./Popover.css";
4
4
  import { Show, createContext, createMemo, createSignal, createTrackedEffect, omit, onSettled, useContext } from "solid-js";
@@ -7,6 +7,7 @@ import { registerOverlay } from "../../lib/overlay.js";
7
7
  import "../_shared/material.css";
8
8
  import { applyBooleanStateRequest } from "../_shared/controlledState.js";
9
9
  import { createOverlayPosition } from "../_shared/overlayPosition.js";
10
+ import { asAriaName, resolvePopoverDialogName } from "./Popover.a11y.js";
10
11
  import { CLASSES, componentRecipe } from "./Popover.recipe.js";
11
12
  var _tmpl$ = /*#__PURE__*/ template("<div>"), _tmpl$2 = /*#__PURE__*/ template("<button>"), _tmpl$3 = /*#__PURE__*/ template('<svg aria-hidden=true data-slot=popover-arrow-svg height=12 viewBox="0 0 12 12"width=12><path d="M0 0C5.48483 8 6.5 8 12 0Z"fill=currentColor>'), _tmpl$4 = /*#__PURE__*/ template("<span>"), _tmpl$5 = /*#__PURE__*/ template("<h3>");
12
13
  const PopoverContext = createContext(null);
@@ -23,6 +24,8 @@ const __solidLayoutPopoverRoot = (_stable, p)=>{
23
24
  const [triggerId] = createSignal(`popover-trigger-${Math.random().toString(36).slice(2, 8)}`);
24
25
  const [contentId] = createSignal(`popover-content-${Math.random().toString(36).slice(2, 8)}`);
25
26
  const [resolvedPlacement, setResolvedPlacement] = createSignal(p.placement ?? "bottom");
27
+ const [dialogLabel, setDialogLabel] = createSignal();
28
+ const [dialogLabelledBy, setDialogLabelledBy] = createSignal();
26
29
  const isControlled = createMemo(()=>void 0 !== p.open);
27
30
  const isOpen = createMemo(()=>isControlled() ? Boolean(p.open) : internalOpen());
28
31
  const setIsOpen = (next, options)=>{
@@ -86,7 +89,11 @@ const __solidLayoutPopoverRoot = (_stable, p)=>{
86
89
  triggerId,
87
90
  contentId,
88
91
  offset,
89
- onInteractOutside: p.onInteractOutside
92
+ onInteractOutside: p.onInteractOutside,
93
+ dialogLabel,
94
+ setDialogLabel,
95
+ dialogLabelledBy,
96
+ setDialogLabelledBy
90
97
  };
91
98
  return createComponent(PopoverContext, {
92
99
  value: ctx,
@@ -127,7 +134,7 @@ const PopoverRoot = defineComponent({
127
134
  ]
128
135
  });
129
136
  const __solidLayoutPopoverTrigger = (_stable, p)=>{
130
- const others = omit(p, "children", "class", "dataTheme", "style", "type", "onClick", "onKeyDown");
137
+ const others = omit(p, "children", "class", "dataTheme", "style", "type", "onClick", "onKeyDown", "as");
131
138
  const ctx = usePopoverContext();
132
139
  const handleClick = (event)=>{
133
140
  if ("function" == typeof p.onClick) p.onClick(event);
@@ -144,44 +151,88 @@ const __solidLayoutPopoverTrigger = (_stable, p)=>{
144
151
  });
145
152
  }
146
153
  };
147
- var _el$2 = _tmpl$2();
148
- ref(()=>(el)=>ctx.setTriggerRef(el), _el$2);
149
- spread(_el$2, mergeProps(others, {
150
- get type () {
151
- return p.type ?? "button";
152
- },
153
- get id () {
154
- return ctx.triggerId();
155
- }
156
- }, ()=>({
157
- class: twMerge(CLASSES.slot.trigger, p.class)
158
- }), {
159
- "data-slot": "popover-trigger",
160
- get ["data-theme"] () {
161
- return p.dataTheme;
162
- },
163
- get style () {
164
- return p.style;
165
- },
166
- "aria-haspopup": "dialog",
167
- get ["aria-expanded"] () {
168
- return ctx.isOpen() ? "true" : "false";
154
+ return createComponent(Show, {
155
+ get when () {
156
+ return p.as;
169
157
  },
170
- get ["aria-controls"] () {
171
- return ctx.isOpen() ? ctx.contentId() : void 0;
158
+ get fallback () {
159
+ var _el$2 = _tmpl$2();
160
+ ref(()=>(el)=>ctx.setTriggerRef(el), _el$2);
161
+ spread(_el$2, mergeProps(others, {
162
+ get type () {
163
+ return p.type ?? "button";
164
+ },
165
+ get id () {
166
+ return ctx.triggerId();
167
+ }
168
+ }, ()=>({
169
+ class: twMerge(CLASSES.slot.trigger, p.class)
170
+ }), {
171
+ "data-slot": "popover-trigger",
172
+ get ["data-theme"] () {
173
+ return p.dataTheme;
174
+ },
175
+ get style () {
176
+ return p.style;
177
+ },
178
+ "aria-haspopup": "dialog",
179
+ get ["aria-expanded"] () {
180
+ return ctx.isOpen() ? "true" : "false";
181
+ },
182
+ get ["aria-controls"] () {
183
+ return ctx.isOpen() ? ctx.contentId() : void 0;
184
+ },
185
+ onClick: handleClick,
186
+ onKeyDown: handleKeyDown
187
+ }), true);
188
+ insert(_el$2, ()=>p.children);
189
+ return _el$2;
172
190
  },
173
- onClick: handleClick,
174
- onKeyDown: handleKeyDown
175
- }), true);
176
- insert(_el$2, ()=>p.children);
177
- return _el$2;
191
+ get children () {
192
+ return createComponent(Dynamic, mergeProps({
193
+ get component () {
194
+ return p.as;
195
+ }
196
+ }, others, {
197
+ ref: (el)=>ctx.setTriggerRef(el),
198
+ get type () {
199
+ return p.type ?? "button";
200
+ },
201
+ get id () {
202
+ return ctx.triggerId();
203
+ },
204
+ get ["class"] () {
205
+ return p.class;
206
+ },
207
+ get ["data-theme"] () {
208
+ return p.dataTheme;
209
+ },
210
+ get style () {
211
+ return p.style;
212
+ },
213
+ "aria-haspopup": "dialog",
214
+ get ["aria-expanded"] () {
215
+ return ctx.isOpen() ? "true" : "false";
216
+ },
217
+ get ["aria-controls"] () {
218
+ return memo(()=>!!ctx.isOpen())() ? ctx.contentId() : void 0;
219
+ },
220
+ onClick: handleClick,
221
+ onKeyDown: handleKeyDown,
222
+ get children () {
223
+ return p.children;
224
+ }
225
+ }));
226
+ }
227
+ });
178
228
  };
179
229
  const PopoverTrigger = defineComponent({
180
230
  recipe: componentRecipe,
181
231
  layout: __solidLayoutPopoverTrigger,
182
232
  embedded: true,
183
233
  behaviour: [
184
- "children"
234
+ "children",
235
+ "as"
185
236
  ]
186
237
  });
187
238
  const __solidLayoutPopoverContent = (_stable, p)=>{
@@ -200,6 +251,13 @@ const __solidLayoutPopoverContent = (_stable, p)=>{
200
251
  createTrackedEffect(()=>{
201
252
  ctx.setPlacement(overlayPosition.placement());
202
253
  });
254
+ const dialogName = ()=>resolvePopoverDialogName({
255
+ contentLabel: asAriaName(p["aria-label"]),
256
+ contentLabelledBy: asAriaName(p["aria-labelledby"]),
257
+ dialogLabel: ctx.dialogLabel(),
258
+ dialogLabelledBy: ctx.dialogLabelledBy(),
259
+ triggerId: ctx.triggerRef() ? ctx.triggerId() : void 0
260
+ });
203
261
  const style = ()=>{
204
262
  const overlayStyle = overlayPosition.style();
205
263
  if ("string" == typeof p.style) return [
@@ -247,8 +305,11 @@ const __solidLayoutPopoverContent = (_stable, p)=>{
247
305
  get style () {
248
306
  return style();
249
307
  },
308
+ get ["aria-label"] () {
309
+ return dialogName()["aria-label"];
310
+ },
250
311
  get ["aria-labelledby"] () {
251
- return p["aria-labelledby"] ?? (ctx.triggerRef() ? ctx.triggerId() : void 0);
312
+ return dialogName()["aria-labelledby"];
252
313
  },
253
314
  get ["aria-hidden"] () {
254
315
  return ctx.isOpen() ? "false" : "true";
@@ -272,7 +333,16 @@ const PopoverContent = defineComponent({
272
333
  ]
273
334
  });
274
335
  const __solidLayoutPopoverDialog = (_stable, p)=>{
275
- const others = omit(p, "children", "class", "dataTheme", "style");
336
+ const others = omit(p, "children", "class", "dataTheme", "style", "aria-label", "aria-labelledby");
337
+ const ctx = usePopoverContext();
338
+ createTrackedEffect(()=>{
339
+ ctx.setDialogLabel(asAriaName(p["aria-label"]));
340
+ return ()=>ctx.setDialogLabel(void 0);
341
+ });
342
+ createTrackedEffect(()=>{
343
+ ctx.setDialogLabelledBy(asAriaName(p["aria-labelledby"]));
344
+ return ()=>ctx.setDialogLabelledBy(void 0);
345
+ });
276
346
  var _el$4 = _tmpl$();
277
347
  spread(_el$4, mergeProps(others, ()=>({
278
348
  class: twMerge(CLASSES.slot.dialog, p.class)
@@ -2,7 +2,7 @@ import type { Component as __LayoutComponent } from "solid-js";
2
2
  import "./RangeCalendar.css";
3
3
  import type { JSX } from "@solidjs/web";
4
4
  import { type ControlledDateRangeValue } from "../../hooks/date";
5
- import { type CalendarWeekdayFormat } from "../calendar";
5
+ import { type CalendarWeekdayFormat, type DateNames } from "../calendar";
6
6
  import type { UIBaseProps, State } from "../vocabulary";
7
7
  export type RangeCalendarValue = ControlledDateRangeValue;
8
8
  type RangeCalendarBaseProps = {
@@ -10,6 +10,8 @@ type RangeCalendarBaseProps = {
10
10
  defaultValue?: RangeCalendarValue;
11
11
  onChange?: (value: RangeCalendarValue) => void;
12
12
  locale?: string;
13
+ /** Month and weekday names for a language other than English. See `Calendar`. */
14
+ dateNames?: DateNames;
13
15
  weekdayFormat?: CalendarWeekdayFormat;
14
16
  minValue?: Date;
15
17
  maxValue?: Date;
@@ -8,7 +8,7 @@ import calendar from "../calendar/index.js";
8
8
  import { CLASSES, componentRecipe } from "./RangeCalendar.recipe.js";
9
9
  var _tmpl$ = /*#__PURE__*/ template("<div>");
10
10
  const __solidLayoutRangeCalendar = (_stable, p)=>{
11
- const others = omit(p, "class", "dataTheme", "style", "ref", "value", "defaultValue", "onChange", "locale", "weekdayFormat", "minValue", "maxValue", "isDateUnavailable", "showOutsideDays", "state", "disabled", "onDaySelect", "onDayHover");
11
+ const others = omit(p, "class", "dataTheme", "style", "ref", "value", "defaultValue", "onChange", "locale", "dateNames", "weekdayFormat", "minValue", "maxValue", "isDateUnavailable", "showOutsideDays", "state", "disabled", "onDaySelect", "onDayHover");
12
12
  const isDisabled = createMemo(()=>Boolean("disabled" === p.state) || Boolean(p.disabled));
13
13
  const rangeSelection = useRangeSelection({
14
14
  value: ()=>p.value,
@@ -63,6 +63,9 @@ const __solidLayoutRangeCalendar = (_stable, p)=>{
63
63
  get locale () {
64
64
  return p.locale;
65
65
  },
66
+ get dateNames () {
67
+ return p.dateNames;
68
+ },
66
69
  get weekdayFormat () {
67
70
  return p.weekdayFormat;
68
71
  },
@@ -23,7 +23,7 @@ const __solidLayoutMobileListView = (_stable, p)=>{
23
23
  get children () {
24
24
  return createComponent(icon, {
25
25
  get src () {
26
- return p.emptyIcon ?? "icon-[mdi--inbox-outline]";
26
+ return p.emptyIcon ?? "icon-[lucide--inbox]";
27
27
  },
28
28
  width: 24,
29
29
  height: 24
@@ -1,6 +1,6 @@
1
1
  import type { Component as __LayoutComponent } from "solid-js";
2
2
  import "./Text.css";
3
- import type { JSX } from "@solidjs/web";
3
+ import { type JSX } from "@solidjs/web";
4
4
  import type { UIBaseProps } from "../vocabulary";
5
5
  export type TextSize = "xs" | "sm" | "base" | "lg" | "xl";
6
6
  export type TextVariant = "default" | "muted" | "subtle" | "success" | "warning" | "danger";
@@ -9,6 +9,16 @@ export type TextTransform = "none" | "uppercase" | "lowercase" | "capitalize";
9
9
  export type TextTracking = "normal" | "wide";
10
10
  export type TextLeading = "normal" | "none";
11
11
  export type TextFamily = "body" | "heading" | "display" | "mono";
12
+ /**
13
+ * The elements `Text` will render as.
14
+ *
15
+ * Deliberately a closed list rather than `keyof JSX.IntrinsicElements`. The
16
+ * point of the prop is to let a title *be* a heading, and an open list makes
17
+ * that one option among two hundred, most of which are wrong for a run of
18
+ * text. Six heading levels, the three neutral containers, and the two
19
+ * inline emphases that carry meaning of their own.
20
+ */
21
+ export type TextAs = "span" | "p" | "div" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "strong" | "em";
12
22
  export type TextRootProps = Omit<JSX.HTMLAttributes<HTMLSpanElement>, "color"> & UIBaseProps & {
13
23
  size?: TextSize;
14
24
  variant?: TextVariant;
@@ -17,6 +27,21 @@ export type TextRootProps = Omit<JSX.HTMLAttributes<HTMLSpanElement>, "color"> &
17
27
  tracking?: TextTracking;
18
28
  leading?: TextLeading;
19
29
  family?: TextFamily;
30
+ /**
31
+ * The element this text is.
32
+ *
33
+ * `span` by default, which is what it has always been and what a run of
34
+ * text inside a sentence should stay. Reach for this whenever the text is
35
+ * structural: `as="h1"` for a page title, `as="h2"` for a section, `as="p"`
36
+ * for a paragraph.
37
+ *
38
+ * `family="heading"` is a *typeface*: it says which face to set the text
39
+ * in, and it has never said anything about the document. Sites read the
40
+ * two as one thing and styled titles that were not headings, so entire
41
+ * pages, landing pages included, had no heading of any role on them and no
42
+ * way for a reader to move between sections. This is the axis that says so.
43
+ */
44
+ as?: TextAs;
20
45
  children?: JSX.Element;
21
46
  };
22
47
  declare const TextRoot: __LayoutComponent<TextRootProps>;
@@ -1,15 +1,18 @@
1
- import { insert, mergeProps, spread, template } from "@solidjs/web";
1
+ import { Dynamic, createComponent, mergeProps } from "@solidjs/web";
2
2
  import { defineComponent } from "solid-layouts/solid-2/application-boundary";
3
3
  import "./Text.css";
4
4
  import { omit } from "solid-js";
5
5
  import { componentRecipe } from "./Text.recipe.js";
6
- var _tmpl$ = /*#__PURE__*/ template("<span>");
7
6
  const __solidLayoutTextRoot = (_stable, p)=>{
8
- const others = omit(p, "children", "class", "dataTheme", "style", "size", "variant", "weight", "transform", "tracking", "leading", "family");
7
+ const others = omit(p, "children", "class", "dataTheme", "style", "size", "variant", "weight", "transform", "tracking", "leading", "family", "as");
9
8
  const size = ()=>p.size ?? "base";
10
9
  const variant = ()=>p.variant ?? "default";
11
- var _el$ = _tmpl$();
12
- spread(_el$, mergeProps(others, ()=>_stable.slot.root, {
10
+ const tag = ()=>p.as ?? "span";
11
+ return createComponent(Dynamic, mergeProps({
12
+ get component () {
13
+ return tag();
14
+ }
15
+ }, others, ()=>_stable.slot.root, {
13
16
  get ["data-size"] () {
14
17
  return size();
15
18
  },
@@ -36,10 +39,11 @@ const __solidLayoutTextRoot = (_stable, p)=>{
36
39
  },
37
40
  get style () {
38
41
  return p.style;
42
+ },
43
+ get children () {
44
+ return p.children;
39
45
  }
40
- }), true);
41
- insert(_el$, ()=>p.children);
42
- return _el$;
46
+ }));
43
47
  };
44
48
  const TextRoot = defineComponent({
45
49
  recipe: componentRecipe,
@@ -53,6 +57,7 @@ const TextRoot = defineComponent({
53
57
  "tracking",
54
58
  "leading",
55
59
  "family",
60
+ "as",
56
61
  "children"
57
62
  ]
58
63
  });
@@ -1 +1 @@
1
- export { default, Text, type TextFamily, type TextLeading, type TextProps, TextRoot, type TextRootProps, type TextSize, type TextTracking, type TextTransform, type TextVariant, type TextWeight, } from "./Text.generated";
1
+ export { default, Text, type TextAs, type TextFamily, type TextLeading, type TextProps, TextRoot, type TextRootProps, type TextSize, type TextTracking, type TextTransform, type TextVariant, type TextWeight, } from "./Text.generated";
@@ -141,7 +141,7 @@ const __solidLayoutThemeColorPicker = (_stable, p)=>{
141
141
  },
142
142
  get children () {
143
143
  return p.children ?? createComponent(icon, mergeProps({
144
- src: "icon-[mdi--palette]",
144
+ src: "icon-[lucide--palette]",
145
145
  width: 16,
146
146
  height: 16
147
147
  }, ()=>({
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Month and weekday names, and the four date strings the calendar assembles
3
+ * from them, without `Intl`.
4
+ *
5
+ * `Intl` is not defined in the chuzz browser. That is deliberate policy and
6
+ * the decision has been made not to ship ICU data, so `new Intl.DateTimeFormat`
7
+ * is not a formatter that returns a bad date, it is a `ReferenceError`. The
8
+ * throw escapes every boundary the component has and reaches Solid 2, which
9
+ * responds by halting its reactive system permanently: the page keeps painting
10
+ * the frame it already had, so it looks alive, while every control on it is
11
+ * dead. js.software's `/calendar` route is dead this way today, and nothing on
12
+ * the page says so.
13
+ *
14
+ * What the calendar actually needed from `Intl` was twelve month names, seven
15
+ * weekday names in three widths, and four assembly patterns. That is a table,
16
+ * so this is the table.
17
+ *
18
+ * ## The locale question
19
+ *
20
+ * Only `en-US` is carried here. A library that shipped name tables for every
21
+ * locale would be shipping ICU data by another name, which is the thing that
22
+ * was ruled out.
23
+ *
24
+ * The sites are not all English, so the names are an input: pass `dateNames`
25
+ * and the calendar renders yours. A site that already ships five locales
26
+ * through its own i18n has these strings; it hands over the set for the
27
+ * language it is currently in.
28
+ *
29
+ * When a consumer passes `locale="de-DE"` and no `dateNames`, the calendar
30
+ * renders English. It cannot do anything else, and the two dishonest options
31
+ * were to throw or to keep claiming German while showing "June". So it renders
32
+ * English *and says so*: `resolveDateNames` returns the locale actually
33
+ * rendered, and `Calendar` puts that on the root as `lang`. A page asking for
34
+ * German gets `lang="en-US"`, which is true, which an assistive technology
35
+ * reads correctly, and which a check can catch.
36
+ */
37
+ /** Weekday name width, matching the widths `Intl` calls `weekday`. */
38
+ export type DateNameWidth = "narrow" | "short" | "long";
39
+ /**
40
+ * The names a calendar needs to render.
41
+ *
42
+ * Every array is indexed positionally: months by `Date#getMonth` (0 is
43
+ * January), weekdays by `Date#getDay` (0 is Sunday). All five are required.
44
+ * A partial set was the friendlier API and the wrong one: German months beside
45
+ * English weekdays under `lang="de-DE"` is exactly the half-truth this module
46
+ * exists to avoid.
47
+ */
48
+ export type DateNames = {
49
+ /** 12 entries, `getMonth`-indexed. "January". */
50
+ monthsLong: readonly string[];
51
+ /** 12 entries, `getMonth`-indexed. "Jan". */
52
+ monthsShort: readonly string[];
53
+ /** 7 entries, `getDay`-indexed from Sunday. "S". */
54
+ weekdaysNarrow: readonly string[];
55
+ /** 7 entries, `getDay`-indexed from Sunday. "Sun". */
56
+ weekdaysShort: readonly string[];
57
+ /** 7 entries, `getDay`-indexed from Sunday. "Sunday". */
58
+ weekdaysLong: readonly string[];
59
+ };
60
+ /** The locale the built-in table transcribes. */
61
+ export declare const DEFAULT_DATE_LOCALE = "en-US";
62
+ /**
63
+ * `en-US`, transcribed from `Intl` rather than typed from memory.
64
+ *
65
+ * `tests/hooks/date/date-names.test.ts` regenerates every entry from a real
66
+ * `Intl.DateTimeFormat` under bun's ICU and compares, so a typo here is a test
67
+ * failure and not a wrong month in production.
68
+ */
69
+ export declare const EN_US_DATE_NAMES: DateNames;
70
+ /**
71
+ * Pick the names to render with, and report the locale that will actually be
72
+ * on screen.
73
+ *
74
+ * The returned `locale` is the honest one, not the requested one: it is the
75
+ * consumer's locale only when the consumer supplied the names to back it.
76
+ */
77
+ export declare const resolveDateNames: (locale: string | undefined, names: DateNames | undefined) => {
78
+ names: DateNames;
79
+ locale: string;
80
+ };
81
+ /**
82
+ * The grid heading. `{ month: "long", year: "numeric" }` in `en-US`.
83
+ *
84
+ * "June 2025".
85
+ */
86
+ export declare const formatMonthYear: (date: Date, names?: DateNames) => string;
87
+ /**
88
+ * A day button's `aria-label`. `{ dateStyle: "full" }` in `en-US`.
89
+ *
90
+ * "Sunday, June 15, 2025": long weekday, comma, long month, space, the day
91
+ * with no leading zero, comma, year.
92
+ */
93
+ export declare const formatFullDate: (date: Date, names?: DateNames) => string;
94
+ /**
95
+ * A picker's trigger text. `{ day: "numeric", month: "short", year: "numeric" }`
96
+ * in `en-US`.
97
+ *
98
+ * "Jun 15, 2025". Note that `Intl` orders this month-first for `en-US`
99
+ * regardless of the order the options are written in.
100
+ */
101
+ export declare const formatCompactDate: (date: Date, names?: DateNames) => string;
102
+ /**
103
+ * The seven column headers, Sunday first. `{ weekday: width }` in `en-US`.
104
+ *
105
+ * `["Sun", "Mon", ...]` at the default `short` width.
106
+ */
107
+ export declare const weekdayNames: (width: DateNameWidth, names?: DateNames) => readonly string[];
@@ -0,0 +1,74 @@
1
+ const DEFAULT_DATE_LOCALE = "en-US";
2
+ const EN_US_DATE_NAMES = {
3
+ monthsLong: [
4
+ "January",
5
+ "February",
6
+ "March",
7
+ "April",
8
+ "May",
9
+ "June",
10
+ "July",
11
+ "August",
12
+ "September",
13
+ "October",
14
+ "November",
15
+ "December"
16
+ ],
17
+ monthsShort: [
18
+ "Jan",
19
+ "Feb",
20
+ "Mar",
21
+ "Apr",
22
+ "May",
23
+ "Jun",
24
+ "Jul",
25
+ "Aug",
26
+ "Sep",
27
+ "Oct",
28
+ "Nov",
29
+ "Dec"
30
+ ],
31
+ weekdaysNarrow: [
32
+ "S",
33
+ "M",
34
+ "T",
35
+ "W",
36
+ "T",
37
+ "F",
38
+ "S"
39
+ ],
40
+ weekdaysShort: [
41
+ "Sun",
42
+ "Mon",
43
+ "Tue",
44
+ "Wed",
45
+ "Thu",
46
+ "Fri",
47
+ "Sat"
48
+ ],
49
+ weekdaysLong: [
50
+ "Sunday",
51
+ "Monday",
52
+ "Tuesday",
53
+ "Wednesday",
54
+ "Thursday",
55
+ "Friday",
56
+ "Saturday"
57
+ ]
58
+ };
59
+ const resolveDateNames = (locale, names)=>names ? {
60
+ names,
61
+ locale: locale ?? DEFAULT_DATE_LOCALE
62
+ } : {
63
+ names: EN_US_DATE_NAMES,
64
+ locale: DEFAULT_DATE_LOCALE
65
+ };
66
+ const formatMonthYear = (date, names = EN_US_DATE_NAMES)=>`${names.monthsLong[date.getMonth()] ?? ""} ${date.getFullYear()}`;
67
+ const formatFullDate = (date, names = EN_US_DATE_NAMES)=>`${names.weekdaysLong[date.getDay()] ?? ""}, ${names.monthsLong[date.getMonth()] ?? ""} ${date.getDate()}, ${date.getFullYear()}`;
68
+ const formatCompactDate = (date, names = EN_US_DATE_NAMES)=>`${names.monthsShort[date.getMonth()] ?? ""} ${date.getDate()}, ${date.getFullYear()}`;
69
+ const weekdayNames = (width, names = EN_US_DATE_NAMES)=>{
70
+ if ("narrow" === width) return names.weekdaysNarrow;
71
+ if ("long" === width) return names.weekdaysLong;
72
+ return names.weekdaysShort;
73
+ };
74
+ export { DEFAULT_DATE_LOCALE, EN_US_DATE_NAMES, formatCompactDate, formatFullDate, formatMonthYear, resolveDateNames, weekdayNames };
@@ -1,3 +1,4 @@
1
+ import { type DateNames } from "./date.names";
1
2
  export declare const DAYS_PER_WEEK = 7;
2
3
  export declare const CALENDAR_GRID_DAYS = 42;
3
4
  export type DateRangeValue = {
@@ -8,7 +9,16 @@ export declare const createCalendarDate: (year: number, monthIndex: number, day:
8
9
  export declare const normalizeDate: (value: Date | null | undefined) => Date | null;
9
10
  export declare const getToday: () => Date;
10
11
  export declare const parseDate: (value: string | null | undefined) => Date | null;
11
- export declare const formatDate: (value: Date | null | undefined, locale?: string, options?: Intl.DateTimeFormatOptions) => string;
12
+ /**
13
+ * The date a picker shows on its trigger. "Jun 15, 2025".
14
+ *
15
+ * The second parameter used to be a locale string and the third an
16
+ * `Intl.DateTimeFormatOptions`. Both are gone rather than kept as ignored
17
+ * arguments: a `locale` this function cannot honour is worse than no `locale`
18
+ * at all, and the component that owns the locale prop resolves it through
19
+ * `resolveDateNames` before calling here. See `date.names.ts`.
20
+ */
21
+ export declare const formatDate: (value: Date | null | undefined, names?: DateNames) => string;
12
22
  export declare const toISODate: (value: Date | null | undefined) => string;
13
23
  export declare const compareDates: (left: Date, right: Date) => number;
14
24
  export declare const compareMonths: (left: Date, right: Date) => number;
@@ -1,3 +1,4 @@
1
+ import { EN_US_DATE_NAMES, formatCompactDate } from "./date.names.js";
1
2
  const DAYS_PER_WEEK = 7;
2
3
  const CALENDAR_GRID_DAYS = 42;
3
4
  const ISO_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
@@ -23,14 +24,10 @@ const parseDate = (value)=>{
23
24
  if (parsed.getFullYear() !== year || parsed.getMonth() !== month - 1 || parsed.getDate() !== day) return null;
24
25
  return parsed;
25
26
  };
26
- const formatDate = (value, locale = "en-US", options = {
27
- day: "numeric",
28
- month: "short",
29
- year: "numeric"
30
- })=>{
27
+ const formatDate = (value, names = EN_US_DATE_NAMES)=>{
31
28
  const date = normalizeDate(value);
32
29
  if (!date) return "";
33
- return new Intl.DateTimeFormat(locale, options).format(date);
30
+ return formatCompactDate(date, names);
34
31
  };
35
32
  const pad = (value)=>String(value).padStart(2, "0");
36
33
  const toISODate = (value)=>{
@@ -1,3 +1,4 @@
1
+ export * from "./date.names";
1
2
  export * from "./date.utils";
2
3
  export * from "./useCalendarNavigation";
3
4
  export * from "./useCalendarState";
@@ -1,3 +1,4 @@
1
+ export * from "./date.names.js";
1
2
  export * from "./date.utils.js";
2
3
  export * from "./useCalendarNavigation.js";
3
4
  export * from "./useCalendarState.js";
@@ -1,9 +1,16 @@
1
1
  import { type Accessor } from "solid-js";
2
+ import { type DateNameWidth, type DateNames } from "./date.names";
2
3
  export type CalendarSelectionMode = "single" | "range";
3
4
  type CalendarStateOptions = {
4
5
  selectionMode: Accessor<CalendarSelectionMode>;
5
- locale: Accessor<string>;
6
- weekdayFormat: Accessor<"narrow" | "short" | "long">;
6
+ /**
7
+ * The names to render with. The owning component resolves these from its
8
+ * `locale` and `dateNames` props through `resolveDateNames`, so there is no
9
+ * `locale` here: this hook formats from a table and has nothing to do with
10
+ * one. See `date.names.ts`.
11
+ */
12
+ dateNames: Accessor<DateNames>;
13
+ weekdayFormat: Accessor<DateNameWidth>;
7
14
  visibleMonth: Accessor<Date>;
8
15
  focusedDate: Accessor<Date>;
9
16
  selectedDate: Accessor<Date | null>;
@@ -28,9 +35,9 @@ export type CalendarCellState = {
28
35
  isAriaSelected: boolean;
29
36
  };
30
37
  export declare const useCalendarState: (options: CalendarStateOptions) => {
31
- monthFormatter: import("solid-js").SourceAccessor<Intl.DateTimeFormat>;
32
- dayLabelFormatter: import("solid-js").SourceAccessor<Intl.DateTimeFormat>;
33
- weekdayLabels: import("solid-js").SourceAccessor<string[]>;
38
+ monthLabel: import("solid-js").SourceAccessor<string>;
39
+ formatDayLabel: (date: Date) => string;
40
+ weekdayLabels: import("solid-js").SourceAccessor<readonly string[]>;
34
41
  calendarWeeks: import("solid-js").SourceAccessor<Date[][]>;
35
42
  normalizedRange: import("solid-js").SourceAccessor<{
36
43
  start: Date | null;