najm-kit 2.11.13 → 2.11.15

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.
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { e as NajmMode, a as NajmDesignConfig } from './design-types-Rkpt8Pg1.js';
3
+ import { b as NajmMode, N as NajmDesignConfig } from './design-types-DffH_9XC.js';
4
4
  import { b as NTablePaginationLabels, N as NajmTranslate } from './paginationLabels-dZLSNxfo.js';
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
6
6
  import { VariantProps } from 'class-variance-authority';
@@ -3,11 +3,11 @@ import { Translations } from 'najm-i18n';
3
3
  import { F as FormDevToolsOptions, N as NBrandingInput } from '../NNotFoundState-CgU76llk.js';
4
4
  export { a as NEmptyState, b as NEmptyStateProps, c as NErrorState, d as NErrorStateProps, e as NFeedbackSurface, f as NForbiddenState, g as NForbiddenStateProps, h as NLoadingState, i as NLoadingStateProps, j as NNotFoundState, k as NNotFoundStateProps } from '../NNotFoundState-CgU76llk.js';
5
5
  import { NajmNextUIProviderProps } from './next.js';
6
- export { D as DEFAULT_FEEDBACK_KEY_PREFIX, F as FeedbackKey, N as NFeedbackDefaults, a as NFeedbackLabelKeys, b as NFeedbackLabels } from '../NajmUIProvider-DTyRrukg.js';
6
+ export { D as DEFAULT_FEEDBACK_KEY_PREFIX, F as FeedbackKey, N as NFeedbackDefaults, a as NFeedbackLabelKeys, b as NFeedbackLabels } from '../NajmUIProvider-BwL5HFIV.js';
7
7
  import 'zod';
8
8
  import 'lucide-react';
9
9
  import 'next/image';
10
- import '../design-types-Rkpt8Pg1.js';
10
+ import '../design-types-DffH_9XC.js';
11
11
  import '../paginationLabels-dZLSNxfo.js';
12
12
  import 'class-variance-authority/types';
13
13
  import 'class-variance-authority';
@@ -1,7 +1,7 @@
1
1
  import React__default from 'react';
2
2
  import { ImageProps } from 'next/image';
3
- import { c as NajmUIProviderProps } from '../NajmUIProvider-DTyRrukg.js';
4
- import '../design-types-Rkpt8Pg1.js';
3
+ import { c as NajmUIProviderProps } from '../NajmUIProvider-BwL5HFIV.js';
4
+ import '../design-types-DffH_9XC.js';
5
5
  import '../paginationLabels-dZLSNxfo.js';
6
6
  import 'class-variance-authority/types';
7
7
  import 'class-variance-authority';
@@ -0,0 +1,28 @@
1
+ // src/lib/timeZones.ts
2
+ var NAJM_TIME_ZONES = [
3
+ "UTC",
4
+ "Atlantic/Azores",
5
+ "America/Los_Angeles",
6
+ "America/Denver",
7
+ "America/Chicago",
8
+ "America/New_York",
9
+ "America/Sao_Paulo",
10
+ "Pacific/Honolulu",
11
+ "Europe/London",
12
+ "Europe/Paris",
13
+ "Africa/Casablanca",
14
+ "Africa/Tunis",
15
+ "Africa/Cairo",
16
+ "Africa/Nairobi",
17
+ "Asia/Riyadh",
18
+ "Asia/Dubai",
19
+ "Asia/Kolkata",
20
+ "Asia/Bangkok",
21
+ "Asia/Shanghai",
22
+ "Asia/Tokyo",
23
+ "Australia/Sydney",
24
+ "Pacific/Auckland"
25
+ ];
26
+ var NAJM_DEFAULT_TIME_ZONE = "UTC";
27
+
28
+ export { NAJM_DEFAULT_TIME_ZONE, NAJM_TIME_ZONES };
@@ -1,4 +1,4 @@
1
- import { a as NajmDesignConfig, d as NajmVariantStyle } from './design-types-Rkpt8Pg1.js';
1
+ import { N as NajmDesignConfig, a as NajmVariantStyle } from './design-types-DffH_9XC.js';
2
2
 
3
3
  /** Keeps authored TypeScript design objects type-checked without changing them. */
4
4
  declare function defineNajmDesignConfig(config: NajmDesignConfig): NajmDesignConfig;
@@ -156,4 +156,4 @@ declare const NAJM_COMPONENT_NAMES: readonly NajmComponentName[];
156
156
  declare const RADIUS_VALUE_MAP: Record<string, string>;
157
157
  declare function resolveRadiusValue(radius: NajmComponentRadius | undefined): string | undefined;
158
158
 
159
- export { type NajmComponentThemeConfig as N, RADIUS_VALUE_MAP as R, type NajmDesignConfig as a, type NajmLayoutConfig as b, type NajmTypographyConfig as c, type NajmVariantStyle as d, type NajmMode as e, type NajmThemeProviderProps as f, type NajmAppearance as g, type NajmThemeConfig as h, type NajmAccent as i, type NajmThemeTokens as j, type NajmPreset as k, type NajmComponentName as l, type NajmComponentStyleConfig as m, type NajmResponsiveBreakpoint as n, type NajmResponsiveValue as o, NAJM_COMPONENT_NAMES as p, type NajmComponentRadius as q, type NajmDensity as r, type NajmSlotStyle as s, resolveRadiusValue as t };
159
+ export { type NajmDesignConfig as N, RADIUS_VALUE_MAP as R, type NajmVariantStyle as a, type NajmMode as b, type NajmComponentThemeConfig as c, type NajmLayoutConfig as d, type NajmTypographyConfig as e, type NajmThemeProviderProps as f, type NajmAppearance as g, type NajmThemeConfig as h, type NajmAccent as i, type NajmThemeTokens as j, type NajmPreset as k, type NajmComponentName as l, type NajmComponentStyleConfig as m, type NajmResponsiveBreakpoint as n, type NajmResponsiveValue as o, NAJM_COMPONENT_NAMES as p, type NajmComponentRadius as q, type NajmDensity as r, type NajmSlotStyle as s, resolveRadiusValue as t };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import * as React$1 from 'react';
2
2
  import React__default, { RefObject, ReactNode, ComponentType, InputHTMLAttributes, Ref, ImgHTMLAttributes, CSSProperties, MouseEvent, MouseEventHandler } from 'react';
3
- import { a as NajmDesignConfig, f as NajmThemeProviderProps, g as NajmAppearance, e as NajmMode, h as NajmThemeConfig, i as NajmAccent, j as NajmThemeTokens, k as NajmPreset, l as NajmComponentName, m as NajmComponentStyleConfig, N as NajmComponentThemeConfig, c as NajmTypographyConfig, b as NajmLayoutConfig, n as NajmResponsiveBreakpoint, o as NajmResponsiveValue } from './design-types-Rkpt8Pg1.js';
4
- export { p as NAJM_COMPONENT_NAMES, q as NajmComponentRadius, r as NajmDensity, s as NajmSlotStyle, d as NajmVariantStyle, R as RADIUS_VALUE_MAP, t as resolveRadiusValue } from './design-types-Rkpt8Pg1.js';
5
- import { d as NTableToolbarLabels, e as NIconSource, B as BadgeColor, f as BadgeShape } from './NajmUIProvider-DTyRrukg.js';
6
- export { g as Badge, h as BadgeIcon, i as BadgeLook, j as BadgeProps, k as BadgeSize, l as BadgeVariant, D as DEFAULT_FEEDBACK_KEY_PREFIX, m as DEFAULT_TIME_ZONE, F as FeedbackKey, n as NBadge, o as NBadgeDefaults, p as NBadgeLook, q as NBadgeProps, N as NFeedbackDefaults, a as NFeedbackLabelKeys, b as NFeedbackLabels, r as NIcon, s as NIconProps, t as NTableDefaults, u as NTableDefaultsProvider, v as NajmPreferencesContextValue, w as NajmPreferencesProvider, x as NajmPreferencesProviderProps, y as NajmUIProvider, c as NajmUIProviderProps, z as badgeColorVariants, A as badgeVariants, C as useDocumentDirection, E as useNTableDefaults, G as useNajmPreferencesContext, H as useNajmTheme, I as useNajmTimeZone, J as useResolvedToolbarLabels } from './NajmUIProvider-DTyRrukg.js';
3
+ import { N as NajmDesignConfig, f as NajmThemeProviderProps, g as NajmAppearance, b as NajmMode, h as NajmThemeConfig, i as NajmAccent, j as NajmThemeTokens, k as NajmPreset, l as NajmComponentName, m as NajmComponentStyleConfig, c as NajmComponentThemeConfig, e as NajmTypographyConfig, d as NajmLayoutConfig, n as NajmResponsiveBreakpoint, o as NajmResponsiveValue } from './design-types-DffH_9XC.js';
4
+ export { p as NAJM_COMPONENT_NAMES, q as NajmComponentRadius, r as NajmDensity, s as NajmSlotStyle, a as NajmVariantStyle, R as RADIUS_VALUE_MAP, t as resolveRadiusValue } from './design-types-DffH_9XC.js';
5
+ import { d as NTableToolbarLabels, e as NIconSource, B as BadgeColor, f as BadgeShape } from './NajmUIProvider-BwL5HFIV.js';
6
+ export { g as Badge, h as BadgeIcon, i as BadgeLook, j as BadgeProps, k as BadgeSize, l as BadgeVariant, D as DEFAULT_FEEDBACK_KEY_PREFIX, m as DEFAULT_TIME_ZONE, F as FeedbackKey, n as NBadge, o as NBadgeDefaults, p as NBadgeLook, q as NBadgeProps, N as NFeedbackDefaults, a as NFeedbackLabelKeys, b as NFeedbackLabels, r as NIcon, s as NIconProps, t as NTableDefaults, u as NTableDefaultsProvider, v as NajmPreferencesContextValue, w as NajmPreferencesProvider, x as NajmPreferencesProviderProps, y as NajmUIProvider, c as NajmUIProviderProps, z as badgeColorVariants, A as badgeVariants, C as useDocumentDirection, E as useNTableDefaults, G as useNajmPreferencesContext, H as useNajmTheme, I as useNajmTimeZone, J as useResolvedToolbarLabels } from './NajmUIProvider-BwL5HFIV.js';
7
7
  import { N as NajmTranslate, c as NTablePaginationVariant, b as NTablePaginationLabels, a as NTableCardPagination } from './paginationLabels-dZLSNxfo.js';
8
8
  export { D as DEFAULT_PAGINATION_KEY_PREFIX, d as NTableInfinitePagination, e as NTableLoadMorePagination, f as buildPaginationLabels } from './paginationLabels-dZLSNxfo.js';
9
- export { d as defineNajmDesignConfig, p as parseNajmDesignConfig, r as resolveVariantAlias, s as stringifyNajmDesignConfig } from './design-config-D_x1GCu3.js';
9
+ export { d as defineNajmDesignConfig, p as parseNajmDesignConfig, r as resolveVariantAlias, s as stringifyNajmDesignConfig } from './design-config-CL9vqqQ9.js';
10
10
  import * as class_variance_authority_types from 'class-variance-authority/types';
11
11
  import { VariantProps } from 'class-variance-authority';
12
12
  import * as LabelPrimitive from '@radix-ui/react-label';
package/dist/index.mjs CHANGED
@@ -15,6 +15,7 @@ import './chunk-F5KXJCCJ.mjs';
15
15
  import { humanizeToken } from './chunk-JABLSOQN.mjs';
16
16
  export { DEFAULT_PLACEHOLDER, formatCurrency, formatDate, formatDateTime, formatNumber, formatPercent, formatRelativeTime, formatTime, humanizeToken, localDateInput, slugify } from './chunk-JABLSOQN.mjs';
17
17
  export { DEFAULT_MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, cleanQuery, createOffsetPagination, fetchOffsetPage, getPageIndex } from './chunk-2NX2VKS2.mjs';
18
+ import { NAJM_TIME_ZONES } from './chunk-XGIYOEWD.mjs';
18
19
  import { resolveRadiusValue, resolveVariantAlias, parseNajmDesignConfig } from './chunk-TFHWLE7N.mjs';
19
20
  export { NAJM_COMPONENT_NAMES, RADIUS_VALUE_MAP, defineNajmDesignConfig, defineNajmThemeConfig, parseNajmDesignConfig, parseNajmThemeConfig, resolveRadiusValue, resolveVariantAlias, stringifyNajmDesignConfig, stringifyNajmThemeConfig } from './chunk-TFHWLE7N.mjs';
20
21
  import * as React66 from 'react';
@@ -28,7 +29,7 @@ import { HexColorPicker } from 'react-colorful';
28
29
  import * as PopoverPrimitive from '@radix-ui/react-popover';
29
30
  import { converter, parse, formatHsl, formatRgb, formatHex } from 'culori';
30
31
  import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
31
- import { Command as Command$1 } from 'cmdk';
32
+ import { Command as Command$1, defaultFilter } from 'cmdk';
32
33
  import * as SheetPrimitive from '@radix-ui/react-dialog';
33
34
  import { create } from 'zustand';
34
35
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
@@ -4563,21 +4564,39 @@ var ComboboxInput = ({ placeholder = "Select...", searchPlaceholder = "Search...
4563
4564
  const displayLabel = selectedItem?.label ?? (allowFreeText && value ? value : "");
4564
4565
  const shouldDisplayIcon = Boolean(icon) && showIcon && !value;
4565
4566
  const iconProps = getIconColorProps(iconColor, "h-4 w-4");
4567
+ const [activeItem, setActiveItem] = useState(selectedItem?.label ?? normalizedItems[0]?.label ?? "");
4566
4568
  const trimmedQuery = query.trim();
4567
4569
  const queryMatchesItem = trimmedQuery !== "" && normalizedItems.some((i) => i.value === trimmedQuery || i.label.toLowerCase() === trimmedQuery.toLowerCase());
4568
4570
  const showFreeTextOption = allowFreeText && trimmedQuery !== "" && !queryMatchesItem;
4571
+ const rankItems = (search) => normalizedItems.map((item, index) => ({
4572
+ index,
4573
+ item,
4574
+ score: shouldFilter ? defaultFilter(item.label, search.trim(), [item.value, item.label]) : 1
4575
+ })).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score || left.index - right.index);
4569
4576
  const commit = (next) => {
4570
4577
  onChange(next);
4578
+ setActiveItem(normalizedItems.find((item) => item.value === next)?.label ?? "");
4571
4579
  setOpen(false);
4572
4580
  setQuery("");
4573
4581
  onSearchChange?.("");
4574
4582
  };
4575
4583
  const updateQuery = (next) => {
4584
+ setActiveItem(rankItems(next)[0]?.item.label ?? "");
4576
4585
  setQuery(next);
4577
4586
  onSearchChange?.(next);
4578
4587
  };
4588
+ const updateActiveItem = (next) => {
4589
+ const nextItem = normalizedItems.find((item) => item.label === next);
4590
+ if (trimmedQuery !== "" && shouldFilter && (!nextItem || defaultFilter(nextItem.label, trimmedQuery, [nextItem.value, nextItem.label]) <= 0)) {
4591
+ return;
4592
+ }
4593
+ setActiveItem(next);
4594
+ };
4579
4595
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: (o) => {
4580
4596
  setOpen(o);
4597
+ if (o) {
4598
+ setActiveItem(selectedItem?.label ?? normalizedItems[0]?.label ?? "");
4599
+ }
4581
4600
  if (!o) {
4582
4601
  setQuery("");
4583
4602
  onSearchChange?.("");
@@ -4595,6 +4614,11 @@ var ComboboxInput = ({ placeholder = "Select...", searchPlaceholder = "Search...
4595
4614
  "aria-label": ariaLabel,
4596
4615
  tabIndex: disabled ? -1 : 0,
4597
4616
  "aria-expanded": open,
4617
+ onKeyDown: (event) => {
4618
+ if (disabled || event.key !== "Enter" && event.key !== " ") return;
4619
+ event.preventDefault();
4620
+ setOpen(true);
4621
+ },
4598
4622
  className: cn(
4599
4623
  "cursor-pointer outline-none data-[state=open]:border-ring",
4600
4624
  className
@@ -4620,38 +4644,69 @@ var ComboboxInput = ({ placeholder = "Select...", searchPlaceholder = "Search...
4620
4644
  pointerDismissedRef.current = false;
4621
4645
  triggerRef.current?.blur();
4622
4646
  },
4623
- children: /* @__PURE__ */ jsxs(Command, { shouldFilter, children: [
4624
- /* @__PURE__ */ jsx(
4625
- CommandInput,
4626
- {
4627
- placeholder: searchPlaceholder,
4628
- className: "h-9",
4629
- value: query,
4630
- onValueChange: updateQuery,
4631
- onKeyDown: (e) => {
4632
- if (e.key === "Enter" && showFreeTextOption) {
4633
- e.preventDefault();
4634
- commit(trimmedQuery);
4647
+ children: /* @__PURE__ */ jsxs(
4648
+ Command,
4649
+ {
4650
+ shouldFilter,
4651
+ value: activeItem,
4652
+ onValueChange: updateActiveItem,
4653
+ children: [
4654
+ /* @__PURE__ */ jsx(
4655
+ CommandInput,
4656
+ {
4657
+ placeholder: searchPlaceholder,
4658
+ className: "h-9",
4659
+ value: query,
4660
+ onValueChange: updateQuery,
4661
+ onKeyDown: (e) => {
4662
+ const rankedItems = rankItems(trimmedQuery);
4663
+ if (trimmedQuery !== "" && rankedItems.length > 0 && ["ArrowDown", "ArrowUp", "Home", "End"].includes(e.key)) {
4664
+ e.preventDefault();
4665
+ e.stopPropagation();
4666
+ const currentIndex = rankedItems.findIndex(({ item }) => item.label === activeItem);
4667
+ const nextIndex = e.key === "Home" ? 0 : e.key === "End" ? rankedItems.length - 1 : e.key === "ArrowDown" ? Math.min(currentIndex + 1, rankedItems.length - 1) : Math.max(currentIndex < 0 ? 0 : currentIndex - 1, 0);
4668
+ setActiveItem(rankedItems[nextIndex]?.item.label ?? "");
4669
+ return;
4670
+ }
4671
+ if (e.key !== "Enter") return;
4672
+ if (showFreeTextOption) {
4673
+ e.preventDefault();
4674
+ commit(trimmedQuery);
4675
+ return;
4676
+ }
4677
+ if (trimmedQuery !== "") {
4678
+ const activeMatch = normalizedItems.find((item) => item.label === activeItem);
4679
+ const nextItem = activeMatch && defaultFilter(
4680
+ activeMatch.label,
4681
+ trimmedQuery,
4682
+ [activeMatch.value, activeMatch.label]
4683
+ ) > 0 ? activeMatch : rankedItems[0]?.item;
4684
+ if (nextItem) {
4685
+ e.preventDefault();
4686
+ commit(nextItem.value);
4687
+ }
4688
+ }
4689
+ }
4635
4690
  }
4636
- }
4637
- }
4638
- ),
4639
- /* @__PURE__ */ jsxs(CommandList, { children: [
4640
- /* @__PURE__ */ jsx(CommandEmpty, { children: loading ? loadingMessage : emptyMessage }),
4641
- /* @__PURE__ */ jsxs(CommandGroup, { children: [
4642
- showFreeTextOption && /* @__PURE__ */ jsxs(CommandItem, { value: trimmedQuery, onSelect: () => commit(trimmedQuery), className: "cursor-pointer", children: [
4643
- /* @__PURE__ */ jsx(Check, { className: "mr-2 h-4 w-4 opacity-0" }),
4644
- 'Use "',
4645
- trimmedQuery,
4646
- '"'
4647
- ] }, "__free_text__"),
4648
- normalizedItems.map((item) => /* @__PURE__ */ jsxs(CommandItem, { value: item.label, keywords: [item.value, item.label], onSelect: () => commit(value === item.value ? "" : item.value), className: "cursor-pointer", children: [
4649
- /* @__PURE__ */ jsx(Check, { className: cn("mr-2 h-4 w-4", value === item.value ? "opacity-100" : "opacity-0") }),
4650
- item.label
4651
- ] }, item.value))
4652
- ] })
4653
- ] })
4654
- ] })
4691
+ ),
4692
+ /* @__PURE__ */ jsxs(CommandList, { children: [
4693
+ /* @__PURE__ */ jsx(CommandEmpty, { children: loading ? loadingMessage : emptyMessage }),
4694
+ /* @__PURE__ */ jsxs(CommandGroup, { children: [
4695
+ showFreeTextOption && /* @__PURE__ */ jsxs(CommandItem, { value: trimmedQuery, onSelect: () => commit(trimmedQuery), className: "cursor-pointer", children: [
4696
+ /* @__PURE__ */ jsx(Check, { className: "mr-2 h-4 w-4 opacity-0" }),
4697
+ 'Use "',
4698
+ trimmedQuery,
4699
+ '"'
4700
+ ] }, "__free_text__"),
4701
+ normalizedItems.map((item) => /* @__PURE__ */ jsxs(CommandItem, { value: item.label, keywords: [item.value, item.label], onSelect: () => commit(value === item.value ? "" : item.value), className: "cursor-pointer", children: [
4702
+ /* @__PURE__ */ jsx(Check, { className: cn("mr-2 h-4 w-4", value === item.value ? "opacity-100" : "opacity-0") }),
4703
+ item.label
4704
+ ] }, item.value))
4705
+ ] })
4706
+ ] })
4707
+ ]
4708
+ }
4709
+ )
4655
4710
  }
4656
4711
  )
4657
4712
  ] });
@@ -6206,30 +6261,6 @@ var TimeInput = React66__default.forwardRef(({ value = "", onChange, placeholder
6206
6261
  ] });
6207
6262
  });
6208
6263
  TimeInput.displayName = "TimeInput";
6209
- var timeZoneValues = [
6210
- "UTC",
6211
- "Atlantic/Azores",
6212
- "America/Los_Angeles",
6213
- "America/Denver",
6214
- "America/Chicago",
6215
- "America/New_York",
6216
- "America/Sao_Paulo",
6217
- "Pacific/Honolulu",
6218
- "Europe/London",
6219
- "Europe/Paris",
6220
- "Africa/Casablanca",
6221
- "Africa/Tunis",
6222
- "Africa/Cairo",
6223
- "Africa/Nairobi",
6224
- "Asia/Riyadh",
6225
- "Asia/Dubai",
6226
- "Asia/Kolkata",
6227
- "Asia/Bangkok",
6228
- "Asia/Shanghai",
6229
- "Asia/Tokyo",
6230
- "Australia/Sydney",
6231
- "Pacific/Auckland"
6232
- ];
6233
6264
  var timeZoneFormatter = new Intl.DateTimeFormat("en-US", {
6234
6265
  timeZone: "UTC",
6235
6266
  timeZoneName: "longOffset"
@@ -6241,7 +6272,7 @@ function getTimeZoneLabel(timeZone) {
6241
6272
  }).formatToParts(/* @__PURE__ */ new Date()).find((part) => part.type === "timeZoneName")?.value ?? "GMT";
6242
6273
  return `${timeZone} (${offset})`;
6243
6274
  }
6244
- var timeZones = timeZoneValues.map((value) => ({
6275
+ var timeZones = NAJM_TIME_ZONES.map((value) => ({
6245
6276
  value,
6246
6277
  label: getTimeZoneLabel(value)
6247
6278
  }));
@@ -1,3 +1,160 @@
1
- export { U as UiBootstrapConfig, a as UiBootstrapDiagnostic, b as UiBootstrapFailureReason, c as UiBootstrapFetcher, d as UiBootstrapLoader, e as UiBootstrapResource, f as UiBootstrapResources, g as UiBootstrapSnapshot, h as UiBootstrapValue, i as createUiBootstrapLoader } from '../uiBootstrap-Ci6K5j5t.js';
2
- export { d as defineNajmDesignConfig, p as parseNajmDesignConfig, s as stringifyNajmDesignConfig } from '../design-config-D_x1GCu3.js';
3
- export { N as NajmComponentThemeConfig, a as NajmDesignConfig, b as NajmLayoutConfig, c as NajmTypographyConfig } from '../design-types-Rkpt8Pg1.js';
1
+ export { b as UiBootstrapConfig, c as UiBootstrapDiagnostic, d as UiBootstrapFailureReason, e as UiBootstrapFetcher, h as UiBootstrapLoader, f as UiBootstrapResource, U as UiBootstrapResources, a as UiBootstrapSnapshot, g as UiBootstrapValue, i as createUiBootstrapLoader } from '../uiBootstrap-C66AFCo_.js';
2
+ import { b as NajmMode } from '../design-types-DffH_9XC.js';
3
+ export { c as NajmComponentThemeConfig, N as NajmDesignConfig, d as NajmLayoutConfig, e as NajmTypographyConfig } from '../design-types-DffH_9XC.js';
4
+ export { d as defineNajmDesignConfig, p as parseNajmDesignConfig, s as stringifyNajmDesignConfig } from '../design-config-CL9vqqQ9.js';
5
+
6
+ /**
7
+ * IANA zone identifiers offered by `TimeZoneInput` and accepted by the default
8
+ * preference handlers.
9
+ *
10
+ * Broad enough to cover the regions Najm applications ship to, deliberately
11
+ * not the full IANA database — a 400-entry combobox is not a usable control,
12
+ * and every entry here is a value the server contract must keep accepting.
13
+ * Each one is verified against `Intl.DateTimeFormat` in the package tests, so
14
+ * a typo cannot be published.
15
+ */
16
+ declare const NAJM_TIME_ZONES: readonly ["UTC", "Atlantic/Azores", "America/Los_Angeles", "America/Denver", "America/Chicago", "America/New_York", "America/Sao_Paulo", "Pacific/Honolulu", "Europe/London", "Europe/Paris", "Africa/Casablanca", "Africa/Tunis", "Africa/Cairo", "Africa/Nairobi", "Asia/Riyadh", "Asia/Dubai", "Asia/Kolkata", "Asia/Bangkok", "Asia/Shanghai", "Asia/Tokyo", "Australia/Sydney", "Pacific/Auckland"];
17
+ /** One of the canonical zones. Applications with a custom list infer their own. */
18
+ type NajmTimeZone = (typeof NAJM_TIME_ZONES)[number];
19
+ /** The zone assumed when an application configures none. */
20
+ declare const NAJM_DEFAULT_TIME_ZONE: NajmTimeZone;
21
+
22
+ /**
23
+ * The part of an i18n definition this contract needs.
24
+ *
25
+ * Structural on purpose: `najm-i18n` is an *optional* peer of this package, and
26
+ * a preference handler must not be the reason a consumer has to install it. A
27
+ * `najm-i18n` definition satisfies this shape as it is.
28
+ */
29
+ interface NajmPreferenceI18n<Language extends string = string> {
30
+ readonly supportedLanguages: readonly Language[];
31
+ readonly defaultLanguage: Language;
32
+ normalizeLanguage(value: unknown): Language;
33
+ }
34
+ /** Cookie names, one per preference. */
35
+ interface NajmPreferenceCookieNames {
36
+ language: string;
37
+ theme: string;
38
+ timeZone: string;
39
+ }
40
+ /**
41
+ * Cookie attributes, applied to all three.
42
+ *
43
+ * `httpOnly` is the default because nothing in the browser reads these back —
44
+ * the client provider holds the value it just set, and the server reads the
45
+ * cookie. `secure` is left unset by default rather than `true`: these cookies
46
+ * must survive `http://localhost`, and a deployment terminating TLS at the
47
+ * edge sees no difference. Applications serving only HTTPS should set it.
48
+ */
49
+ interface NajmPreferenceCookieOptions {
50
+ httpOnly: boolean;
51
+ maxAge: number;
52
+ path: string;
53
+ sameSite: "lax" | "strict" | "none";
54
+ secure?: boolean;
55
+ domain?: string;
56
+ }
57
+ /**
58
+ * Anything shaped like Next's cookie store.
59
+ *
60
+ * Structural rather than an import: this file must stay free of `next`, and a
61
+ * test can pass `{ get: (name) => ... }` without building a request.
62
+ */
63
+ interface NajmCookieReader {
64
+ get(name: string): {
65
+ value: string;
66
+ } | undefined;
67
+ }
68
+ interface NajmPreferenceSnapshot<Language extends string = string, TimeZone extends string = NajmTimeZone> {
69
+ language: Language;
70
+ theme: NajmMode;
71
+ timeZone: TimeZone;
72
+ }
73
+ interface NajmPreferenceResolveOptions {
74
+ /**
75
+ * Used only when the language cookie is absent or holds an unsupported
76
+ * value — a signed-in user's stored language, typically. Never overrides a
77
+ * valid cookie: the cookie is what the user last chose in this browser.
78
+ */
79
+ languageFallback?: unknown;
80
+ }
81
+ /** A route handler, ready to `export const POST = ...`. */
82
+ type NajmPreferenceHandler = (request: Request) => Promise<Response>;
83
+ interface NajmPreferenceHandlers {
84
+ /** Reads `{ language }`. */
85
+ language: NajmPreferenceHandler;
86
+ /** Reads `{ theme }`. */
87
+ theme: NajmPreferenceHandler;
88
+ /** Reads `{ timeZone }`. */
89
+ timeZone: NajmPreferenceHandler;
90
+ }
91
+ interface NajmPreferencesConfig<Language extends string = string, TimeZone extends string = NajmTimeZone> {
92
+ /** The application's catalog definition. The one required field. */
93
+ i18n: NajmPreferenceI18n<Language>;
94
+ /**
95
+ * Zones this application accepts. Defaults to the canonical list that
96
+ * `TimeZoneInput` offers.
97
+ *
98
+ * Pass this *only* alongside a matching `items` on the input. Two lists that
99
+ * disagree is the bug the shared default exists to prevent.
100
+ */
101
+ timeZones?: readonly TimeZone[];
102
+ /**
103
+ * Defaults to `UTC`, or to the first configured zone if `UTC` is not in it.
104
+ *
105
+ * `NoInfer` so this field cannot narrow `TimeZone`. Without it, an
106
+ * application that names `Africa/Casablanca` and takes the canonical list
107
+ * gets a definition typed as accepting *only* Casablanca — the opposite of
108
+ * what it configured, and a type error at every other zone downstream.
109
+ */
110
+ defaultTimeZone?: NoInfer<TimeZone>;
111
+ /** Defaults to `light`. */
112
+ defaultTheme?: NajmMode;
113
+ /** Merged over the `najm-ui-*` defaults, per key. */
114
+ cookieNames?: Partial<NajmPreferenceCookieNames>;
115
+ /** Merged over the secure defaults, per key. */
116
+ cookieOptions?: Partial<NajmPreferenceCookieOptions>;
117
+ /** Body field names, if this application's client posts something else. */
118
+ fields?: Partial<Record<"language" | "theme" | "timeZone", string>>;
119
+ /** Rejection messages, per preference. The defaults are generic and safe. */
120
+ messages?: Partial<Record<"language" | "theme" | "timeZone", string>>;
121
+ }
122
+ interface NajmPreferences<Language extends string = string, TimeZone extends string = NajmTimeZone> {
123
+ readonly cookieNames: Readonly<NajmPreferenceCookieNames>;
124
+ readonly cookieOptions: Readonly<NajmPreferenceCookieOptions>;
125
+ readonly timeZones: readonly TimeZone[];
126
+ readonly defaultTimeZone: TimeZone;
127
+ readonly defaultTheme: NajmMode;
128
+ readonly defaultLanguage: Language;
129
+ /** Every preference for this request, resolved from cookies. */
130
+ resolve(cookies: NajmCookieReader, options?: NajmPreferenceResolveOptions): NajmPreferenceSnapshot<Language, TimeZone>;
131
+ handlers: NajmPreferenceHandlers;
132
+ }
133
+ /** The language of a configured definition, so applications alias nothing. */
134
+ type NajmPreferenceLanguage<P> = P extends NajmPreferences<infer Language, string> ? Language : never;
135
+ /** The time zone of a configured definition. */
136
+ type NajmPreferenceTimeZone<P> = P extends NajmPreferences<string, infer TimeZone> ? TimeZone : never;
137
+ /**
138
+ * Configures the preference contract for one application.
139
+ *
140
+ * @example Zero configuration beyond the catalog
141
+ * ```ts
142
+ * export const preferences = defineNajmPreferences({ i18n: appI18n });
143
+ * ```
144
+ *
145
+ * @example An application with published cookie names to keep
146
+ * ```ts
147
+ * export const preferences = defineNajmPreferences({
148
+ * i18n: appI18n,
149
+ * defaultTimeZone: "Africa/Casablanca",
150
+ * cookieNames: {
151
+ * language: "app-ui-language",
152
+ * theme: "app-ui-theme",
153
+ * timeZone: "app-ui-timezone",
154
+ * },
155
+ * });
156
+ * ```
157
+ */
158
+ declare function defineNajmPreferences<Language extends string, const TimeZone extends string = NajmTimeZone>(config: NajmPreferencesConfig<Language, TimeZone>): NajmPreferences<Language, TimeZone>;
159
+
160
+ export { NAJM_DEFAULT_TIME_ZONE, NAJM_TIME_ZONES, type NajmCookieReader, NajmMode, type NajmPreferenceCookieNames, type NajmPreferenceCookieOptions, type NajmPreferenceHandler, type NajmPreferenceHandlers, type NajmPreferenceI18n, type NajmPreferenceLanguage, type NajmPreferenceResolveOptions, type NajmPreferenceSnapshot, type NajmPreferenceTimeZone, type NajmPreferences, type NajmPreferencesConfig, type NajmTimeZone, defineNajmPreferences };
@@ -1,2 +1,131 @@
1
1
  export { createUiBootstrapLoader } from '../chunk-7H6NFBQT.mjs';
2
+ import { NAJM_TIME_ZONES, NAJM_DEFAULT_TIME_ZONE } from '../chunk-XGIYOEWD.mjs';
3
+ export { NAJM_DEFAULT_TIME_ZONE, NAJM_TIME_ZONES } from '../chunk-XGIYOEWD.mjs';
2
4
  export { defineNajmDesignConfig, parseNajmDesignConfig, stringifyNajmDesignConfig } from '../chunk-TFHWLE7N.mjs';
5
+
6
+ // src/server/preferences.ts
7
+ var THEME_MODES = ["light", "dark"];
8
+ var DEFAULT_THEME = "light";
9
+ var ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
10
+ var DEFAULT_COOKIE_NAMES = {
11
+ language: "najm-ui-language",
12
+ theme: "najm-ui-theme",
13
+ timeZone: "najm-ui-timezone"
14
+ };
15
+ var DEFAULT_COOKIE_OPTIONS = {
16
+ httpOnly: true,
17
+ maxAge: ONE_YEAR_IN_SECONDS,
18
+ path: "/",
19
+ sameSite: "lax"
20
+ };
21
+ var COOKIE_NAME_PATTERN = /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/;
22
+ var COOKIE_VALUE_PATTERN = /^[A-Za-z0-9!#$%&'()*+\-./:<=>?@[\]^_`|~]*$/;
23
+ function serializeCookie(name, value, options) {
24
+ if (!COOKIE_NAME_PATTERN.test(name) || !COOKIE_VALUE_PATTERN.test(value)) {
25
+ throw new Error("najm-kit/server: refusing to serialize an unsafe preference cookie");
26
+ }
27
+ const parts = [`${name}=${value}`, `Path=${options.path}`, `Max-Age=${options.maxAge}`];
28
+ if (options.domain) parts.push(`Domain=${options.domain}`);
29
+ if (options.httpOnly) parts.push("HttpOnly");
30
+ if (options.secure) parts.push("Secure");
31
+ parts.push(`SameSite=${options.sameSite[0].toUpperCase()}${options.sameSite.slice(1)}`);
32
+ return parts.join("; ");
33
+ }
34
+ function isRecord(value) {
35
+ return typeof value === "object" && value !== null;
36
+ }
37
+ async function readField(request, field) {
38
+ const body = await request.json().catch(() => null);
39
+ return isRecord(body) ? body[field] : void 0;
40
+ }
41
+ function rejection(message) {
42
+ return Response.json({ message }, { status: 400 });
43
+ }
44
+ function accepted(field, value, cookie) {
45
+ return Response.json({ [field]: value }, { headers: { "Set-Cookie": cookie } });
46
+ }
47
+ function defineNajmPreferences(config) {
48
+ const { i18n } = config;
49
+ const timeZones = Object.freeze([
50
+ ...config.timeZones ?? NAJM_TIME_ZONES
51
+ ]);
52
+ if (timeZones.length === 0) {
53
+ throw new Error("najm-kit/server: `timeZones` must list at least one zone");
54
+ }
55
+ const timeZoneSet = new Set(timeZones);
56
+ const fallbackTimeZone = timeZoneSet.has(NAJM_DEFAULT_TIME_ZONE) ? NAJM_DEFAULT_TIME_ZONE : timeZones[0];
57
+ const defaultTimeZone = config.defaultTimeZone ?? fallbackTimeZone;
58
+ if (!timeZoneSet.has(defaultTimeZone)) {
59
+ throw new Error(
60
+ `najm-kit/server: default time zone "${defaultTimeZone}" is not one of the configured zones`
61
+ );
62
+ }
63
+ const defaultTheme = config.defaultTheme ?? DEFAULT_THEME;
64
+ if (!THEME_MODES.includes(defaultTheme)) {
65
+ throw new Error(`najm-kit/server: default theme "${defaultTheme}" is not a supported mode`);
66
+ }
67
+ const cookieNames = Object.freeze({ ...DEFAULT_COOKIE_NAMES, ...config.cookieNames });
68
+ const cookieOptions = Object.freeze({ ...DEFAULT_COOKIE_OPTIONS, ...config.cookieOptions });
69
+ const fields = {
70
+ language: config.fields?.language ?? "language",
71
+ theme: config.fields?.theme ?? "theme",
72
+ timeZone: config.fields?.timeZone ?? "timeZone"
73
+ };
74
+ const messages = {
75
+ language: config.messages?.language ?? "Unsupported language.",
76
+ theme: config.messages?.theme ?? "Unsupported theme.",
77
+ timeZone: config.messages?.timeZone ?? "Unsupported time zone."
78
+ };
79
+ const isTheme = (value) => typeof value === "string" && THEME_MODES.includes(value);
80
+ const isTimeZone = (value) => typeof value === "string" && timeZoneSet.has(value);
81
+ const isLanguage = (value) => typeof value === "string" && i18n.supportedLanguages.includes(value);
82
+ function resolve(cookies, options = {}) {
83
+ const languageCookie = cookies.get(cookieNames.language)?.value;
84
+ const language = isLanguage(languageCookie) ? languageCookie : i18n.normalizeLanguage(options.languageFallback);
85
+ const themeCookie = cookies.get(cookieNames.theme)?.value;
86
+ const timeZoneCookie = cookies.get(cookieNames.timeZone)?.value;
87
+ return {
88
+ language,
89
+ theme: isTheme(themeCookie) ? themeCookie : defaultTheme,
90
+ timeZone: isTimeZone(timeZoneCookie) ? timeZoneCookie : defaultTimeZone
91
+ };
92
+ }
93
+ const handlers = {
94
+ async language(request) {
95
+ const value = await readField(request, fields.language);
96
+ if (!isLanguage(value)) return rejection(messages.language);
97
+ const language = i18n.normalizeLanguage(value);
98
+ return accepted(
99
+ fields.language,
100
+ language,
101
+ serializeCookie(cookieNames.language, language, cookieOptions)
102
+ );
103
+ },
104
+ async theme(request) {
105
+ const value = await readField(request, fields.theme);
106
+ if (!isTheme(value)) return rejection(messages.theme);
107
+ return accepted(fields.theme, value, serializeCookie(cookieNames.theme, value, cookieOptions));
108
+ },
109
+ async timeZone(request) {
110
+ const value = await readField(request, fields.timeZone);
111
+ if (!isTimeZone(value)) return rejection(messages.timeZone);
112
+ return accepted(
113
+ fields.timeZone,
114
+ value,
115
+ serializeCookie(cookieNames.timeZone, value, cookieOptions)
116
+ );
117
+ }
118
+ };
119
+ return Object.freeze({
120
+ cookieNames,
121
+ cookieOptions,
122
+ timeZones,
123
+ defaultTimeZone,
124
+ defaultTheme,
125
+ defaultLanguage: i18n.defaultLanguage,
126
+ resolve,
127
+ handlers: Object.freeze(handlers)
128
+ });
129
+ }
130
+
131
+ export { defineNajmPreferences };
@@ -1,5 +1,5 @@
1
- import { f as UiBootstrapResources, g as UiBootstrapSnapshot, U as UiBootstrapConfig } from '../uiBootstrap-Ci6K5j5t.js';
2
- export { a as UiBootstrapDiagnostic, b as UiBootstrapFailureReason, c as UiBootstrapFetcher, e as UiBootstrapResource, h as UiBootstrapValue } from '../uiBootstrap-Ci6K5j5t.js';
1
+ import { U as UiBootstrapResources, a as UiBootstrapSnapshot, b as UiBootstrapConfig } from '../uiBootstrap-C66AFCo_.js';
2
+ export { c as UiBootstrapDiagnostic, d as UiBootstrapFailureReason, e as UiBootstrapFetcher, f as UiBootstrapResource, g as UiBootstrapValue } from '../uiBootstrap-C66AFCo_.js';
3
3
 
4
4
  interface ReactServerUiBootstrap<R extends UiBootstrapResources> {
5
5
  /** Every resource for this request, resolved once and shared. */
package/dist/theme.css CHANGED
@@ -317,10 +317,10 @@ input:autofill {
317
317
  overflow-x: auto;
318
318
  scrollbar-width: none;
319
319
  }
320
- .najm-overlay-scroll-x::-webkit-scrollbar {
321
- display: none;
322
- }
323
-
320
+ .najm-overlay-scroll-x::-webkit-scrollbar {
321
+ display: none;
322
+ }
323
+
324
324
  /* Responsive table actions stay discoverable on touch and tablet layouts.
325
325
  Fine-pointer desktops may keep the quieter hover/focus reveal treatment. */
326
326
  .ntable-card-action {
@@ -377,7 +377,7 @@ input:autofill {
377
377
  .nimage-input-compact-overlay:focus-visible {
378
378
  opacity: 1;
379
379
  }
380
- }
380
+ }
381
381
 
382
382
  /* OverlayScrollbars theme used by the <NajmScroll> component — a thin,
383
383
  translucent slate bar that floats over content with no reserved space.