najm-kit 2.11.14 → 2.11.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.11.16 - 2026-09-04
4
+
5
+ - `NBrandingPayload` accepts a `slots` registry, so `NajmAppProvider` takes what
6
+ `najm-theme`'s `loadServerBranding()` returned as `initialBranding` unchanged
7
+ instead of renaming `sidebarLogoExpanded` and `sidebarLogoCollapsed` at the
8
+ call site. Structural — the kit still does not depend on `najm-theme`.
9
+ Precedence is explicit marks, then flat payload fields, then slots, and only
10
+ the two sidebar keys are read: an auth logo, a hero, and a consumer's own
11
+ slots stay out of the context the sidebar sees.
12
+
13
+ ## 2.11.15 - 2026-09-04
14
+
15
+ - Fixed `ComboboxInput` keyboard interaction. Enter and Space now open its
16
+ focusable trigger, and Enter commits the active filtered result instead of
17
+ doing nothing when the previously active CmdK item was filtered out. Disabled
18
+ comboboxes remain non-activatable.
19
+
3
20
  ## 2.11.14 - 2026-09-04
4
21
 
5
22
  - Added `defineNajmPreferences` to `najm-kit/server`: the language, theme, and
@@ -25,15 +25,28 @@ interface NBrandingValue {
25
25
  interface NBrandingPayload {
26
26
  sidebarLogoExpandedPath?: string | null;
27
27
  sidebarLogoCollapsedPath?: string | null;
28
+ /**
29
+ * A slot registry, as `najm-theme` resolves branding: slot key to path.
30
+ *
31
+ * Read here so a themed application passes what `loadServerBranding()`
32
+ * returned straight through, rather than renaming two of its slots at the
33
+ * one call site that mounts the chrome. Only the two sidebar keys are read,
34
+ * so an auth logo, a hero, and a consumer's own slots stay out of the
35
+ * context the sidebar sees.
36
+ *
37
+ * Structural on purpose: the kit does not depend on `najm-theme`, and the
38
+ * dependency runs the other way.
39
+ */
40
+ slots?: Record<string, string | null>;
28
41
  }
29
42
  /** Resolved marks, an endpoint payload, or both. */
30
43
  type NBrandingInput = NBrandingValue & NBrandingPayload;
31
44
  /**
32
45
  * Projects an input onto the marks `useNBranding` publishes.
33
46
  *
34
- * Explicit marks win over payload fields, so an application already passing
35
- * `logoExpanded` — or a `ReactNode` logo, which no payload can express — is
36
- * unaffected by the wider input type.
47
+ * Explicit marks win over payload fields, which win over slots, so an
48
+ * application already passing `logoExpanded` — or a `ReactNode` logo, which no
49
+ * payload can express — is unaffected by the wider input type.
37
50
  */
38
51
  declare function normalizeBranding(input?: NBrandingInput): NBrandingValue;
39
52
  /** Returns `null` outside a provider, so every consumer stays optional. */
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { Translations } from 'najm-i18n';
3
- import { F as FormDevToolsOptions, N as NBrandingInput } from '../NNotFoundState-CgU76llk.js';
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';
3
+ import { F as FormDevToolsOptions, N as NBrandingInput } from '../NNotFoundState-BmGDW2Bx.js';
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-BmGDW2Bx.js';
5
5
  import { NajmNextUIProviderProps } from './next.js';
6
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';
@@ -1,6 +1,6 @@
1
1
  'use client';
2
- import { FormDevToolsProvider, NBrandingStateProvider, NajmFormatProvider } from '../chunk-26EASWUY.mjs';
3
- export { NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState } from '../chunk-26EASWUY.mjs';
2
+ import { FormDevToolsProvider, NBrandingStateProvider, NajmFormatProvider } from '../chunk-5ZTOCWRA.mjs';
3
+ export { NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState } from '../chunk-5ZTOCWRA.mjs';
4
4
  import { NajmNextUIProvider } from '../chunk-DKHIQFXX.mjs';
5
5
  export { DEFAULT_FEEDBACK_KEY_PREFIX } from '../chunk-BDQK6ECA.mjs';
6
6
  import '../chunk-M5VGBZ7R.mjs';
@@ -10,8 +10,8 @@ import { AlertTriangle, LoaderCircleIcon, LoaderPinwheelIcon, LoaderIcon, Shield
10
10
  function normalizeBranding(input) {
11
11
  if (!input) return {};
12
12
  const value = {};
13
- const expanded = input.logoExpanded ?? input.sidebarLogoExpandedPath;
14
- const collapsed = input.logoCollapsed ?? input.sidebarLogoCollapsedPath;
13
+ const expanded = input.logoExpanded ?? input.sidebarLogoExpandedPath ?? input.slots?.sidebarLogoExpanded;
14
+ const collapsed = input.logoCollapsed ?? input.sidebarLogoCollapsedPath ?? input.slots?.sidebarLogoCollapsed;
15
15
  if (input.appName !== void 0) value.appName = input.appName;
16
16
  if (input.logoFallback !== void 0) value.logoFallback = input.logoFallback;
17
17
  if (input.logoHref !== void 0) value.logoHref = input.logoHref;
package/dist/index.d.ts CHANGED
@@ -24,8 +24,8 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
24
24
  import * as ProgressPrimitive from '@radix-ui/react-progress';
25
25
  import * as SeparatorPrimitive from '@radix-ui/react-separator';
26
26
  import { OverlayScrollbarsComponentProps } from 'overlayscrollbars-react';
27
- import { l as FormDevTools, F as FormDevToolsOptions } from './NNotFoundState-CgU76llk.js';
28
- export { m as FormDevToolsConfig, n as FormFillOverride, o as FormFillOverrides, p as NBrandingEditorValue, N as NBrandingInput, q as NBrandingPayload, r as NBrandingProvider, s as NBrandingStateProvider, t as NBrandingStateProviderProps, u as NBrandingValue, 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, v as buildFormFill, w as normalizeBranding, x as useNBranding, y as useNBrandingEditor } from './NNotFoundState-CgU76llk.js';
27
+ import { l as FormDevTools, F as FormDevToolsOptions } from './NNotFoundState-BmGDW2Bx.js';
28
+ export { m as FormDevToolsConfig, n as FormFillOverride, o as FormFillOverrides, p as NBrandingEditorValue, N as NBrandingInput, q as NBrandingPayload, r as NBrandingProvider, s as NBrandingStateProvider, t as NBrandingStateProviderProps, u as NBrandingValue, 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, v as buildFormFill, w as normalizeBranding, x as useNBranding, y as useNBrandingEditor } from './NNotFoundState-BmGDW2Bx.js';
29
29
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
30
30
  import { Command as Command$1 } from 'cmdk';
31
31
  import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { resolveAvatarSrc } from './chunk-BEGOH366.mjs';
2
2
  export { isPlaceholderAvatar, resolveAvatarSrc } from './chunk-BEGOH366.mjs';
3
- import { NLoadingState, NErrorState, NEmptyState, useResolvedFormDevTools, useNBranding } from './chunk-26EASWUY.mjs';
4
- export { FormDevToolsProvider, NBrandingProvider, NBrandingStateProvider, NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState, NPageLayout, NSpinner, NajmFormatProvider, buildFormFill, normalizeBranding, useNBranding, useNBrandingEditor, useNajmFormat, useNajmFormatContext } from './chunk-26EASWUY.mjs';
3
+ import { NLoadingState, NErrorState, NEmptyState, useResolvedFormDevTools, useNBranding } from './chunk-5ZTOCWRA.mjs';
4
+ export { FormDevToolsProvider, NBrandingProvider, NBrandingStateProvider, NEmptyState, NErrorState, NForbiddenState, NLoadingState, NNotFoundState, NPageLayout, NSpinner, NajmFormatProvider, buildFormFill, normalizeBranding, useNBranding, useNBrandingEditor, useNajmFormat, useNajmFormatContext } from './chunk-5ZTOCWRA.mjs';
5
5
  import { normalizeImageSources, useImageChain, useNBadgeDefaults, mergeBadgeMaps, resolveStatusColor, resolveBadgeStatusLabel, colorTextClass, useResolvedPaginationLabels, useResolvedToolbarLabels } from './chunk-BDQK6ECA.mjs';
6
6
  export { DEFAULT_FEEDBACK_KEY_PREFIX, DEFAULT_PAGINATION_KEY_PREFIX, DEFAULT_TIME_ZONE, DEFAULT_TOOLBAR_KEY_PREFIX, EMPTY_DESIGN, NAJM_COLOR_TEXT_CLASSES, NAJM_STATUS_COLORS, NTableDefaultsProvider, NajmDesignEditorProvider, NajmPreferencesProvider, NajmUIProvider, buildPaginationLabels, buildToolbarLabels, colorTextClass, findStatusColor, normalizeStatusToken, resolveStatusColor, statusTextClass, useDocumentDirection, useNTableDefaults, useNajmDesignEditor, useNajmPreferencesContext, useNajmTheme, useNajmTimeZone, useResolvedToolbarLabels } from './chunk-BDQK6ECA.mjs';
7
7
  import { NajmScroll, useNajmScrollViewport, useTableStore, TableStoreContext, NTableJson } from './chunk-5JVFMOLI.mjs';
@@ -29,7 +29,7 @@ import { HexColorPicker } from 'react-colorful';
29
29
  import * as PopoverPrimitive from '@radix-ui/react-popover';
30
30
  import { converter, parse, formatHsl, formatRgb, formatHex } from 'culori';
31
31
  import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
32
- import { Command as Command$1 } from 'cmdk';
32
+ import { Command as Command$1, defaultFilter } from 'cmdk';
33
33
  import * as SheetPrimitive from '@radix-ui/react-dialog';
34
34
  import { create } from 'zustand';
35
35
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
@@ -4564,21 +4564,39 @@ var ComboboxInput = ({ placeholder = "Select...", searchPlaceholder = "Search...
4564
4564
  const displayLabel = selectedItem?.label ?? (allowFreeText && value ? value : "");
4565
4565
  const shouldDisplayIcon = Boolean(icon) && showIcon && !value;
4566
4566
  const iconProps = getIconColorProps(iconColor, "h-4 w-4");
4567
+ const [activeItem, setActiveItem] = useState(selectedItem?.label ?? normalizedItems[0]?.label ?? "");
4567
4568
  const trimmedQuery = query.trim();
4568
4569
  const queryMatchesItem = trimmedQuery !== "" && normalizedItems.some((i) => i.value === trimmedQuery || i.label.toLowerCase() === trimmedQuery.toLowerCase());
4569
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);
4570
4576
  const commit = (next) => {
4571
4577
  onChange(next);
4578
+ setActiveItem(normalizedItems.find((item) => item.value === next)?.label ?? "");
4572
4579
  setOpen(false);
4573
4580
  setQuery("");
4574
4581
  onSearchChange?.("");
4575
4582
  };
4576
4583
  const updateQuery = (next) => {
4584
+ setActiveItem(rankItems(next)[0]?.item.label ?? "");
4577
4585
  setQuery(next);
4578
4586
  onSearchChange?.(next);
4579
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
+ };
4580
4595
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: (o) => {
4581
4596
  setOpen(o);
4597
+ if (o) {
4598
+ setActiveItem(selectedItem?.label ?? normalizedItems[0]?.label ?? "");
4599
+ }
4582
4600
  if (!o) {
4583
4601
  setQuery("");
4584
4602
  onSearchChange?.("");
@@ -4596,6 +4614,11 @@ var ComboboxInput = ({ placeholder = "Select...", searchPlaceholder = "Search...
4596
4614
  "aria-label": ariaLabel,
4597
4615
  tabIndex: disabled ? -1 : 0,
4598
4616
  "aria-expanded": open,
4617
+ onKeyDown: (event) => {
4618
+ if (disabled || event.key !== "Enter" && event.key !== " ") return;
4619
+ event.preventDefault();
4620
+ setOpen(true);
4621
+ },
4599
4622
  className: cn(
4600
4623
  "cursor-pointer outline-none data-[state=open]:border-ring",
4601
4624
  className
@@ -4621,38 +4644,69 @@ var ComboboxInput = ({ placeholder = "Select...", searchPlaceholder = "Search...
4621
4644
  pointerDismissedRef.current = false;
4622
4645
  triggerRef.current?.blur();
4623
4646
  },
4624
- children: /* @__PURE__ */ jsxs(Command, { shouldFilter, children: [
4625
- /* @__PURE__ */ jsx(
4626
- CommandInput,
4627
- {
4628
- placeholder: searchPlaceholder,
4629
- className: "h-9",
4630
- value: query,
4631
- onValueChange: updateQuery,
4632
- onKeyDown: (e) => {
4633
- if (e.key === "Enter" && showFreeTextOption) {
4634
- e.preventDefault();
4635
- 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
+ }
4636
4690
  }
4637
- }
4638
- }
4639
- ),
4640
- /* @__PURE__ */ jsxs(CommandList, { children: [
4641
- /* @__PURE__ */ jsx(CommandEmpty, { children: loading ? loadingMessage : emptyMessage }),
4642
- /* @__PURE__ */ jsxs(CommandGroup, { children: [
4643
- showFreeTextOption && /* @__PURE__ */ jsxs(CommandItem, { value: trimmedQuery, onSelect: () => commit(trimmedQuery), className: "cursor-pointer", children: [
4644
- /* @__PURE__ */ jsx(Check, { className: "mr-2 h-4 w-4 opacity-0" }),
4645
- 'Use "',
4646
- trimmedQuery,
4647
- '"'
4648
- ] }, "__free_text__"),
4649
- 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: [
4650
- /* @__PURE__ */ jsx(Check, { className: cn("mr-2 h-4 w-4", value === item.value ? "opacity-100" : "opacity-0") }),
4651
- item.label
4652
- ] }, item.value))
4653
- ] })
4654
- ] })
4655
- ] })
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
+ )
4656
4710
  }
4657
4711
  )
4658
4712
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "2.11.14",
3
+ "version": "2.11.16",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Reusable React UI component package for Najm framework",