@korioinc/next-core 2.0.56 → 3.0.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.
package/README.md CHANGED
@@ -204,6 +204,32 @@ export default function Layout({ children }: { children: ReactNode }) {
204
204
  }
205
205
  ```
206
206
 
207
+ The switchers keep theme state, system-theme resolution, locale cookies, and routing inside the kit. Consumer apps can supply catalog-owned labels through the component API. Omitting the props uses concise English defaults.
208
+
209
+ ```tsx
210
+ 'use client';
211
+
212
+ import { LocaleSwitcher, ThemeSwitcher } from '@korioinc/next-core/components';
213
+ import { useLingui } from '@lingui/react/macro';
214
+
215
+ export function PreferenceControls() {
216
+ const { t } = useLingui();
217
+ const themeLabels = {
218
+ light: t`Light`,
219
+ dark: t`Dark`,
220
+ system: t`System`,
221
+ changeTheme: t`Change theme`,
222
+ };
223
+
224
+ return (
225
+ <>
226
+ <ThemeSwitcher labels={themeLabels} />
227
+ <LocaleSwitcher ariaLabel={t`Change language`} />
228
+ </>
229
+ );
230
+ }
231
+ ```
232
+
207
233
  Import the shared styles from app CSS:
208
234
 
209
235
  ```css
@@ -5,7 +5,9 @@ interface LocaleSwitcherProps {
5
5
  * If omitted, defaults to locales enabled in lingui.config.ts.
6
6
  */
7
7
  languages?: string[];
8
+ /** Accessible name for the language menu trigger. */
9
+ ariaLabel?: string;
8
10
  }
9
- export default function LocaleSwitcher({ languages }?: LocaleSwitcherProps): import("react").JSX.Element | null;
11
+ export default function LocaleSwitcher({ languages, ariaLabel }?: LocaleSwitcherProps): import("react").JSX.Element | null;
10
12
  export {};
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/locale-switcher/index.tsx"],"names":[],"mappings":"AAeA,UAAU,mBAAmB;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,SAAS,EAAE,GAAE,mBAAwB,sCAiG7E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/locale-switcher/index.tsx"],"names":[],"mappings":"AAeA,UAAU,mBAAmB;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,SAAS,EAAE,SAA6B,EAAE,GAAE,mBAAwB,sCAmG5G"}
@@ -9,7 +9,7 @@ import { allLanguages } from './language-options';
9
9
  import { resolveVisibleLanguages } from './resolve-visible-languages';
10
10
  import { setCookie } from 'cookies-next/client';
11
11
  import linguiConfig from 'lingui.config';
12
- export default function LocaleSwitcher({ languages } = {}) {
12
+ export default function LocaleSwitcher({ languages, ariaLabel = 'Change language' } = {}) {
13
13
  const router = useRouter();
14
14
  const pathname = usePathname();
15
15
  const { i18n } = useLingui();
@@ -53,5 +53,5 @@ export default function LocaleSwitcher({ languages } = {}) {
53
53
  if (!currentLanguage) {
54
54
  return null;
55
55
  }
56
- return (_jsxs(Menu, { as: 'div', className: 'relative', children: [_jsx(MenuButton, { className: 'glass glass-border-subtle hover:bg-glass-interactive relative inline-flex h-9 w-9 items-center justify-center rounded-md border transition-colors', children: currentLanguage.flag }), _jsx(Transition, { as: Fragment, enter: 'transition ease-out duration-100', enterFrom: 'transform opacity-0 scale-95', enterTo: 'transform opacity-100 scale-100', leave: 'transition ease-in duration-75', leaveFrom: 'transform opacity-100 scale-100', leaveTo: 'transform opacity-0 scale-95', children: _jsx(MenuItems, { anchor: 'bottom end', className: 'glass-surface glass-backdrop-md glass-shadow-sm glass-border-faint z-100 w-48 rounded-md border ring-1 ring-black/5 [--anchor-gap:8px] focus:outline-none', children: _jsx("div", { className: 'py-1', children: availableLanguages.map((language) => (_jsx(MenuItem, { children: _jsxs("button", { onClick: () => changeLanguage(language.code), className: 'data-[focus]:bg-glass-interactive text-foreground group flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors', children: [language.flag, _jsx("span", { className: 'whitespace-nowrap', children: language.name }), currentLocale === language.code && (_jsx("svg", { className: 'ml-auto h-4 w-4', fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', children: _jsx("path", { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M5 13l4 4L19 7' }) }))] }) }, language.code))) }) }) })] }));
56
+ return (_jsxs(Menu, { as: 'div', className: 'relative', children: [_jsx(MenuButton, { "aria-label": ariaLabel, className: 'glass glass-border-subtle hover:bg-glass-interactive relative inline-flex h-9 w-9 items-center justify-center rounded-md border transition-colors', children: currentLanguage.flag }), _jsx(Transition, { as: Fragment, enter: 'transition ease-out duration-100', enterFrom: 'transform opacity-0 scale-95', enterTo: 'transform opacity-100 scale-100', leave: 'transition ease-in duration-75', leaveFrom: 'transform opacity-100 scale-100', leaveTo: 'transform opacity-0 scale-95', children: _jsx(MenuItems, { anchor: 'bottom end', className: 'glass-surface glass-backdrop-md glass-shadow-sm glass-border-faint z-100 w-48 rounded-md border ring-1 ring-black/5 [--anchor-gap:8px] focus:outline-none', children: _jsx("div", { className: 'py-1', children: availableLanguages.map((language) => (_jsx(MenuItem, { children: _jsxs("button", { onClick: () => changeLanguage(language.code), className: 'data-[focus]:bg-glass-interactive text-foreground group flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors', children: [language.flag, _jsx("span", { className: 'whitespace-nowrap', children: language.name }), currentLocale === language.code && (_jsx("svg", { className: 'ml-auto h-4 w-4', fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', children: _jsx("path", { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M5 13l4 4L19 7' }) }))] }) }, language.code))) }) }) })] }));
57
57
  }
@@ -1,4 +1,14 @@
1
1
  import * as React from 'react';
2
- declare const ThemeSwitcher: () => React.JSX.Element;
2
+ interface ThemeSwitcherLabels {
3
+ light: string;
4
+ dark: string;
5
+ system: string;
6
+ changeTheme: string;
7
+ }
8
+ interface ThemeSwitcherProps {
9
+ /** Consumer-owned labels. Omit to use the English defaults. */
10
+ labels?: ThemeSwitcherLabels;
11
+ }
12
+ declare const ThemeSwitcher: ({ labels }?: ThemeSwitcherProps) => React.JSX.Element;
3
13
  export default ThemeSwitcher;
4
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/theme-switcher/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqC/B,QAAA,MAAM,aAAa,yBAsGlB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/theme-switcher/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,UAAU,mBAAmB;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,kBAAkB;IAC1B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAwCD,QAAA,MAAM,aAAa,GAAI,aAA4C,kBAAuB,sBAoGzF,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -2,6 +2,12 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { useTheme } from '../../theme';
5
+ const DEFAULT_THEME_SWITCHER_LABELS = {
6
+ light: 'Light',
7
+ dark: 'Dark',
8
+ system: 'System',
9
+ changeTheme: 'Change theme',
10
+ };
5
11
  const FALLBACK_THEME_ORDER = ['light', 'dark'];
6
12
  const isThemeMode = (value) => value === 'light' || value === 'dark' || value === 'system';
7
13
  const getThemeOrder = (themes) => {
@@ -17,7 +23,7 @@ const normalizeTheme = ({ theme, resolvedTheme, themeOrder, }) => {
17
23
  }
18
24
  return themeOrder[0] ?? 'light';
19
25
  };
20
- const ThemeSwitcher = () => {
26
+ const ThemeSwitcher = ({ labels = DEFAULT_THEME_SWITCHER_LABELS } = {}) => {
21
27
  const { theme, resolvedTheme, setTheme, themes } = useTheme();
22
28
  const [mounted, setMounted] = React.useState(false);
23
29
  const [canAnimateColors, setCanAnimateColors] = React.useState(false);
@@ -30,8 +36,7 @@ const ThemeSwitcher = () => {
30
36
  }, []);
31
37
  const themeOrder = getThemeOrder(themes);
32
38
  const currentTheme = normalizeTheme({ theme, resolvedTheme, themeOrder });
33
- const resolvedThemeLabel = resolvedTheme === 'dark' ? 'Dark' : 'Light';
34
- const currentThemeLabel = currentTheme === 'system' ? `System (${resolvedThemeLabel})` : currentTheme === 'dark' ? 'Dark' : 'Light';
39
+ const currentThemeLabel = currentTheme === 'system' ? labels.system : currentTheme === 'dark' ? labels.dark : labels.light;
35
40
  const toggleTheme = () => {
36
41
  const currentIndex = themeOrder.indexOf(currentTheme);
37
42
  const nextTheme = themeOrder[(currentIndex + 1) % themeOrder.length];
@@ -40,6 +45,6 @@ const ThemeSwitcher = () => {
40
45
  if (!mounted) {
41
46
  return (_jsx("button", { className: 'glass glass-border-subtle relative inline-flex h-9 w-9 items-center justify-center rounded-md border opacity-0', "aria-hidden": 'true', children: _jsx("div", { className: 'h-5 w-5' }) }));
42
47
  }
43
- return (_jsxs("button", { onClick: toggleTheme, className: `glass glass-border-subtle hover:bg-glass-interactive relative inline-flex h-9 w-9 items-center justify-center rounded-md border ${canAnimateColors ? 'transition-colors' : ''}`, "aria-label": `Switch theme (current: ${currentThemeLabel})`, title: `Theme: ${currentThemeLabel} (click to change)`, children: [_jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: `absolute h-5 w-5 transition-all ${currentTheme === 'light' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 rotate-90 opacity-0'}`, children: [_jsx("circle", { cx: '12', cy: '12', r: '5' }), _jsx("line", { x1: '12', y1: '1', x2: '12', y2: '3' }), _jsx("line", { x1: '12', y1: '21', x2: '12', y2: '23' }), _jsx("line", { x1: '4.22', y1: '4.22', x2: '5.64', y2: '5.64' }), _jsx("line", { x1: '18.36', y1: '18.36', x2: '19.78', y2: '19.78' }), _jsx("line", { x1: '1', y1: '12', x2: '3', y2: '12' }), _jsx("line", { x1: '21', y1: '12', x2: '23', y2: '12' }), _jsx("line", { x1: '4.22', y1: '19.78', x2: '5.64', y2: '18.36' }), _jsx("line", { x1: '18.36', y1: '5.64', x2: '19.78', y2: '4.22' })] }), _jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: `absolute h-5 w-5 transition-all ${currentTheme === 'dark' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 -rotate-90 opacity-0'}`, children: _jsx("path", { d: 'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z' }) }), _jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: `absolute h-5 w-5 transition-all ${currentTheme === 'system' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 rotate-90 opacity-0'}`, children: [_jsx("rect", { x: '3', y: '4', width: '18', height: '12', rx: '2' }), _jsx("line", { x1: '8', y1: '20', x2: '16', y2: '20' }), _jsx("line", { x1: '12', y1: '16', x2: '12', y2: '20' })] })] }));
48
+ return (_jsxs("button", { onClick: toggleTheme, className: `glass glass-border-subtle hover:bg-glass-interactive relative inline-flex h-9 w-9 items-center justify-center rounded-md border ${canAnimateColors ? 'transition-colors' : ''}`, "aria-label": `${labels.changeTheme}: ${currentThemeLabel}`, children: [_jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: `absolute h-5 w-5 transition-all ${currentTheme === 'light' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 rotate-90 opacity-0'}`, children: [_jsx("circle", { cx: '12', cy: '12', r: '5' }), _jsx("line", { x1: '12', y1: '1', x2: '12', y2: '3' }), _jsx("line", { x1: '12', y1: '21', x2: '12', y2: '23' }), _jsx("line", { x1: '4.22', y1: '4.22', x2: '5.64', y2: '5.64' }), _jsx("line", { x1: '18.36', y1: '18.36', x2: '19.78', y2: '19.78' }), _jsx("line", { x1: '1', y1: '12', x2: '3', y2: '12' }), _jsx("line", { x1: '21', y1: '12', x2: '23', y2: '12' }), _jsx("line", { x1: '4.22', y1: '19.78', x2: '5.64', y2: '18.36' }), _jsx("line", { x1: '18.36', y1: '5.64', x2: '19.78', y2: '4.22' })] }), _jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: `absolute h-5 w-5 transition-all ${currentTheme === 'dark' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 -rotate-90 opacity-0'}`, children: _jsx("path", { d: 'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z' }) }), _jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: `absolute h-5 w-5 transition-all ${currentTheme === 'system' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 rotate-90 opacity-0'}`, children: [_jsx("rect", { x: '3', y: '4', width: '18', height: '12', rx: '2' }), _jsx("line", { x1: '8', y1: '20', x2: '16', y2: '20' }), _jsx("line", { x1: '12', y1: '16', x2: '12', y2: '20' })] })] }));
44
49
  };
45
50
  export default ThemeSwitcher;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korioinc/next-core",
3
- "version": "2.0.56",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./ads": {
@@ -90,7 +90,7 @@
90
90
  "tw-animate-css": "^1.4.0",
91
91
  "typescript": "^6.0.3",
92
92
  "vitest": "^4.1.10",
93
- "@korioinc/next-configs": "2.0.56"
93
+ "@korioinc/next-configs": "3.0.0"
94
94
  },
95
95
  "dependencies": {
96
96
  "@floating-ui/react": "^0.27.20",
@@ -105,7 +105,7 @@
105
105
  "schema-dts": "^2.0.0",
106
106
  "tailwind-merge": "^3.6.0",
107
107
  "valtio": "^2.3.2",
108
- "@korioinc/next-conf": "2.0.56"
108
+ "@korioinc/next-conf": "3.0.0"
109
109
  },
110
110
  "publishConfig": {
111
111
  "access": "public",