najm-kit 2.11.16 → 2.11.17

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.
@@ -18,20 +18,62 @@ interface NajmAppBranding {
18
18
  logoExpanded?: string | null;
19
19
  logoCollapsed?: string | null;
20
20
  }
21
+ /**
22
+ * The part of a `najm-i18n` definition this provider reads.
23
+ *
24
+ * Structural, so a whole definition satisfies it — its methods are simply
25
+ * extra — and named for data rather than behaviour because everything derived
26
+ * here (the writing direction, the formatting tags) is declared metadata.
27
+ *
28
+ * That also decides what a Server Component may pass. This provider is a
29
+ * Client Component, and React rejects a prop carrying functions outright, so a
30
+ * server layout passes `definition.snapshot` — the same fields without the
31
+ * methods — while a client parent passes the definition itself.
32
+ */
33
+ interface NajmAppI18n {
34
+ translations: Translations;
35
+ defaultLanguage?: string;
36
+ supportedLanguages?: readonly string[];
37
+ fallbackToDefaultLanguage?: boolean;
38
+ languageMetadata?: {
39
+ readonly [language: string]: {
40
+ locale?: string;
41
+ direction?: 'ltr' | 'rtl';
42
+ } | undefined;
43
+ };
44
+ }
21
45
  interface NajmAppProviderProps extends Omit<NajmNextUIProviderProps, 't'> {
22
46
  /**
23
47
  * Enables schema-driven form filling for every `NForm` and `WizardForm`.
24
48
  * `true` uses F8; an options object can choose another shortcut.
25
49
  */
26
50
  formDevTools?: boolean | FormDevToolsOptions;
51
+ /**
52
+ * A `najm-i18n` definition, standing in for five props: `translations`,
53
+ * `defaultLanguage`, `fallbackToDefaultLanguage`, `locales` and
54
+ * `getLanguageDirection`. All five are already declared in the definition, so
55
+ * an application that has one should not have to take it apart and hand the
56
+ * pieces back. Any of them passed explicitly still wins, which is how an
57
+ * application overrides one facet without abandoning the definition.
58
+ *
59
+ * From a Server Component, pass `definition.snapshot` instead — see
60
+ * `NajmAppI18n`.
61
+ *
62
+ * `initialLanguage` is deliberately not part of it: the active language is a
63
+ * per-request value no definition can know.
64
+ */
65
+ i18n?: NajmAppI18n;
27
66
  /**
28
67
  * Catalog for `najm-i18n`. Supplying it mounts an `I18nProvider` and derives
29
68
  * the pagination labels from it, so `t` is not a prop here — the provider
30
69
  * already has the translator and passing one in would be a second source of
31
70
  * truth.
71
+ *
72
+ * Defaults to `i18n.translations`.
32
73
  */
33
74
  translations?: Translations;
34
75
  initialLanguage?: string;
76
+ /** Defaults to `i18n.defaultLanguage`. */
35
77
  defaultLanguage?: string;
36
78
  /**
37
79
  * Resolves a key missing from the active language against `defaultLanguage`
@@ -41,9 +83,17 @@ interface NajmAppProviderProps extends Omit<NajmNextUIProviderProps, 't'> {
41
83
  * Off by default, matching the package. An application shipping an
42
84
  * incomplete locale beside a complete one turns it on here rather than
43
85
  * pre-merging its catalogs.
86
+ *
87
+ * Defaults to `i18n.fallbackToDefaultLanguage`.
44
88
  */
45
89
  fallbackToDefaultLanguage?: boolean;
46
- /** Maps a language to the writing direction applied to `<html>`. */
90
+ /**
91
+ * Maps a language to the writing direction applied to `<html>`.
92
+ *
93
+ * Defaults to `i18n.languageMetadata`, resolving an unsupported language
94
+ * against `defaultLanguage` the way the definition's own `direction` does,
95
+ * rather than falling through to `najm-i18n`'s built-in Arabic guess.
96
+ */
47
97
  getLanguageDirection?: (language: string) => 'ltr' | 'rtl';
48
98
  /**
49
99
  * Where the chosen language is POSTed, as `{ language }`. Defaults to
@@ -77,6 +127,8 @@ interface NajmAppProviderProps extends Omit<NajmNextUIProviderProps, 't'> {
77
127
  * The distinction matters because language and region are separate choices:
78
128
  * `fr` alone formats dates and separators the French way, which is not how
79
129
  * they are written in Morocco. Unmapped languages are used as-is.
130
+ *
131
+ * Defaults to the `locale` of each `i18n.languageMetadata` entry.
80
132
  */
81
133
  locales?: Record<string, string>;
82
134
  /**
@@ -122,6 +174,6 @@ interface NajmAppProviderProps extends Omit<NajmNextUIProviderProps, 't'> {
122
174
  * state machine of its own above this provider. The controlled `design` and
123
175
  * `branding` props still work for applications that already do.
124
176
  */
125
- declare function NajmAppProvider({ translations, initialLanguage, defaultLanguage, fallbackToDefaultLanguage, getLanguageDirection, languageEndpoint, appName, initialBranding, formDevTools, ...props }: NajmAppProviderProps): React.JSX.Element;
177
+ declare function NajmAppProvider({ i18n, translations, initialLanguage, defaultLanguage, fallbackToDefaultLanguage, getLanguageDirection: languageDirection, locales: localeTags, languageEndpoint, appName, initialBranding, formDevTools, ...props }: NajmAppProviderProps): React.JSX.Element;
126
178
 
127
- export { type NajmAppBranding, NajmAppProvider, type NajmAppProviderProps };
179
+ export { type NajmAppBranding, type NajmAppI18n, NajmAppProvider, type NajmAppProviderProps };
@@ -69,17 +69,39 @@ function NajmAppNoI18n({
69
69
  ) });
70
70
  }
71
71
  function NajmAppProvider({
72
- translations,
72
+ i18n,
73
+ translations = i18n?.translations,
73
74
  initialLanguage,
74
- defaultLanguage,
75
- fallbackToDefaultLanguage,
76
- getLanguageDirection,
75
+ defaultLanguage = i18n?.defaultLanguage,
76
+ fallbackToDefaultLanguage = i18n?.fallbackToDefaultLanguage,
77
+ getLanguageDirection: languageDirection,
78
+ locales: localeTags,
77
79
  languageEndpoint = DEFAULT_LANGUAGE_ENDPOINT,
78
80
  appName,
79
81
  initialBranding,
80
82
  formDevTools,
81
83
  ...props
82
84
  }) {
85
+ const getLanguageDirection = React.useMemo(() => {
86
+ if (languageDirection) return languageDirection;
87
+ if (!i18n) return void 0;
88
+ const metadata = i18n.languageMetadata;
89
+ const languages = i18n.supportedLanguages ?? Object.keys(i18n.translations);
90
+ const fallback = i18n.defaultLanguage;
91
+ return (language) => {
92
+ const known = languages.includes(language) ? language : fallback ?? language;
93
+ return metadata?.[known]?.direction ?? "ltr";
94
+ };
95
+ }, [i18n, languageDirection]);
96
+ const locales = React.useMemo(() => {
97
+ if (localeTags) return localeTags;
98
+ if (!i18n?.languageMetadata) return void 0;
99
+ const metadata = i18n.languageMetadata;
100
+ const languages = i18n.supportedLanguages ?? Object.keys(i18n.translations);
101
+ return Object.fromEntries(
102
+ languages.map((language) => [language, metadata[language]?.locale ?? language])
103
+ );
104
+ }, [i18n, localeTags]);
83
105
  const persistLanguage = React.useCallback(
84
106
  async (language) => {
85
107
  const response = await fetch(languageEndpoint, {
@@ -98,7 +120,7 @@ function NajmAppProvider({
98
120
  );
99
121
  const seeded = appName ? { appName, ...initialBranding } : initialBranding;
100
122
  if (!translations) {
101
- return /* @__PURE__ */ jsx(FormDevToolsProvider, { value: formDevTools, children: /* @__PURE__ */ jsx(NajmAppNoI18n, { ...props, initialBranding: seeded }) });
123
+ return /* @__PURE__ */ jsx(FormDevToolsProvider, { value: formDevTools, children: /* @__PURE__ */ jsx(NajmAppNoI18n, { ...props, locales, initialBranding: seeded }) });
102
124
  }
103
125
  return /* @__PURE__ */ jsx(FormDevToolsProvider, { value: formDevTools, children: /* @__PURE__ */ jsx(
104
126
  I18nProvider,
@@ -109,7 +131,7 @@ function NajmAppProvider({
109
131
  fallbackToDefaultLanguage,
110
132
  getLanguageDirection,
111
133
  onLanguageChange: persistLanguage,
112
- children: /* @__PURE__ */ jsx(NajmAppUI, { ...props, initialBranding: seeded })
134
+ children: /* @__PURE__ */ jsx(NajmAppUI, { ...props, locales, initialBranding: seeded })
113
135
  }
114
136
  ) });
115
137
  }
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "2.11.16",
3
+ "version": "2.11.17",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Reusable React UI component package for Najm framework",
@@ -198,7 +198,7 @@
198
198
  "@uiw/react-codemirror": "^4.25.0",
199
199
  "@vitejs/plugin-react": "^5.2.0",
200
200
  "happy-dom": "^20.11.1",
201
- "najm-i18n": "^2.1.1",
201
+ "najm-i18n": "^2.1.2",
202
202
  "postcss": "^8.5.22",
203
203
  "react-hook-form": "^7",
204
204
  "rimraf": "^6",