@enonic/ui-kit 0.5.2 → 0.7.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 +71 -24
- package/dist/dialog/action-dialog-context.d.ts +7 -3
- package/dist/dialog/action-dialog-context.js.map +1 -1
- package/dist/dialog/action-dialog.d.ts +4 -1
- package/dist/dialog/action-dialog.js +69 -27
- package/dist/dialog/action-dialog.js.map +1 -1
- package/dist/dialog/dialog-preset.js +1 -1
- package/dist/dialog/dialog-preset.js.map +1 -1
- package/dist/dialog/use-close-guard.d.ts +3 -3
- package/dist/dialog/use-close-guard.js +3 -3
- package/dist/dialog/use-close-guard.js.map +1 -1
- package/dist/filter/filter-fields.d.ts +1 -1
- package/dist/filter/filter-fields.js.map +1 -1
- package/dist/filter/filter-input.d.ts +2 -2
- package/dist/filter/filter-input.js +14 -17
- package/dist/filter/filter-input.js.map +1 -1
- package/dist/filter/filter-input.phrases.d.ts +1 -6
- package/dist/filter/filter-input.phrases.js +1 -6
- package/dist/filter/filter-input.phrases.js.map +1 -1
- package/dist/i18n/phrases.d.ts +29 -0
- package/dist/i18n/phrases.js +16 -7
- package/dist/i18n/phrases.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +7 -2
- package/dist/notifications/notification-list.d.ts +18 -0
- package/dist/notifications/notification-list.js +92 -0
- package/dist/notifications/notification-list.js.map +1 -0
- package/dist/notifications/notification-list.phrases.d.ts +9 -0
- package/dist/notifications/notification-list.phrases.js +7 -0
- package/dist/notifications/notification-list.phrases.js.map +1 -0
- package/dist/notifications/use-focus-hand-off.js +43 -0
- package/dist/notifications/use-focus-hand-off.js.map +1 -0
- package/dist/theme/theme-switcher.d.ts +14 -0
- package/dist/theme/theme-switcher.js +71 -0
- package/dist/theme/theme-switcher.js.map +1 -0
- package/dist/theme/theme-switcher.phrases.d.ts +17 -0
- package/dist/theme/theme-switcher.phrases.js +17 -0
- package/dist/theme/theme-switcher.phrases.js.map +1 -0
- package/dist/theme/use-resolved-theme.d.ts +10 -0
- package/dist/theme/use-resolved-theme.js +14 -0
- package/dist/theme/use-resolved-theme.js.map +1 -0
- package/package.json +11 -10
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
//#region src/notifications/use-focus-hand-off.ts
|
|
3
|
+
function activeElementAround(element) {
|
|
4
|
+
return element.getRootNode().activeElement;
|
|
5
|
+
}
|
|
6
|
+
function hasFocus(element) {
|
|
7
|
+
return element.contains(activeElementAround(element));
|
|
8
|
+
}
|
|
9
|
+
function useFocusHandOff() {
|
|
10
|
+
const stack = useRef(null);
|
|
11
|
+
const enteredFrom = useRef(null);
|
|
12
|
+
const focusInside = useRef(false);
|
|
13
|
+
const closedByKeyboard = useRef(false);
|
|
14
|
+
const onFocusCapture = ({ relatedTarget }) => {
|
|
15
|
+
const from = relatedTarget instanceof HTMLElement ? relatedTarget : null;
|
|
16
|
+
if (from == null ? !focusInside.current : !stack.current?.contains(from)) enteredFrom.current = from;
|
|
17
|
+
focusInside.current = true;
|
|
18
|
+
};
|
|
19
|
+
const onBlurCapture = ({ relatedTarget }) => {
|
|
20
|
+
if (relatedTarget instanceof Node && !stack.current?.contains(relatedTarget)) focusInside.current = false;
|
|
21
|
+
};
|
|
22
|
+
const onClickCapture = ({ detail }) => {
|
|
23
|
+
closedByKeyboard.current = detail === 0;
|
|
24
|
+
};
|
|
25
|
+
const handOff = (wrapper) => {
|
|
26
|
+
if (wrapper == null || !closedByKeyboard.current || !hasFocus(wrapper)) return;
|
|
27
|
+
const target = (wrapper.nextElementSibling ?? wrapper.previousElementSibling)?.querySelector("button") ?? enteredFrom.current;
|
|
28
|
+
if (target?.isConnected) target.focus();
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
stackProps: {
|
|
32
|
+
ref: stack,
|
|
33
|
+
onFocusCapture,
|
|
34
|
+
onBlurCapture,
|
|
35
|
+
onClickCapture
|
|
36
|
+
},
|
|
37
|
+
handOff
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { hasFocus, useFocusHandOff };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=use-focus-hand-off.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-focus-hand-off.js","names":[],"sources":["../../src/notifications/use-focus-hand-off.ts"],"sourcesContent":["import { type FocusEventHandler, type MouseEventHandler, type Ref, useRef } from 'react';\n\n// Inside a shadow root `document.activeElement` is the host, so the focus is read from the root\n// the element is in.\nfunction activeElementAround(element: Element): Element | null {\n return (element.getRootNode() as Document | ShadowRoot).activeElement;\n}\n\nexport function hasFocus(element: Element): boolean {\n return element.contains(activeElementAround(element));\n}\n\nexport type FocusHandOff = {\n /** Spread on the element that holds the toasts. */\n stackProps: {\n ref: Ref<HTMLDivElement>;\n onFocusCapture: FocusEventHandler<HTMLDivElement>;\n onBlurCapture: FocusEventHandler<HTMLDivElement>;\n onClickCapture: MouseEventHandler<HTMLDivElement>;\n };\n /**\n * Called before a toast's wrapper goes: a toast closed from the keyboard while it has the focus\n * hands it to the toast next to it, or back to where the focus came from when the stack was\n * entered.\n */\n handOff: (wrapper: HTMLElement | undefined) => void;\n};\n\nexport function useFocusHandOff(): FocusHandOff {\n const stack = useRef<HTMLDivElement>(null);\n const enteredFrom = useRef<HTMLElement | null>(null);\n const focusInside = useRef(false);\n // ! A pointer click focuses the button in most browsers, so the focus alone does not tell a\n // ! keyboard close from a mouse one; `detail` is 0 for a key press and an assistive-tech click.\n const closedByKeyboard = useRef(false);\n\n // A window regaining focus reports no `relatedTarget` although the focus never left the stack.\n const onFocusCapture: FocusEventHandler<HTMLDivElement> = ({ relatedTarget }) => {\n const from = relatedTarget instanceof HTMLElement ? relatedTarget : null;\n if (from == null ? !focusInside.current : !stack.current?.contains(from)) {\n enteredFrom.current = from;\n }\n focusInside.current = true;\n };\n\n const onBlurCapture: FocusEventHandler<HTMLDivElement> = ({ relatedTarget }) => {\n if (relatedTarget instanceof Node && !stack.current?.contains(relatedTarget)) {\n focusInside.current = false;\n }\n };\n\n const onClickCapture: MouseEventHandler<HTMLDivElement> = ({ detail }) => {\n closedByKeyboard.current = detail === 0;\n };\n\n const handOff = (wrapper: HTMLElement | undefined): void => {\n if (wrapper == null || !closedByKeyboard.current || !hasFocus(wrapper)) {\n return;\n }\n const neighbour = wrapper.nextElementSibling ?? wrapper.previousElementSibling;\n const target = neighbour?.querySelector('button') ?? enteredFrom.current;\n if (target?.isConnected) {\n target.focus();\n }\n };\n\n return {\n stackProps: { ref: stack, onFocusCapture, onBlurCapture, onClickCapture },\n handOff,\n };\n}\n"],"mappings":";;AAIA,SAAS,oBAAoB,SAAkC;CAC7D,OAAQ,QAAQ,YAAY,CAAC,CAA2B;AAC1D;AAEA,SAAgB,SAAS,SAA2B;CAClD,OAAO,QAAQ,SAAS,oBAAoB,OAAO,CAAC;AACtD;AAkBA,SAAgB,kBAAgC;CAC9C,MAAM,QAAQ,OAAuB,IAAI;CACzC,MAAM,cAAc,OAA2B,IAAI;CACnD,MAAM,cAAc,OAAO,KAAK;CAGhC,MAAM,mBAAmB,OAAO,KAAK;CAGrC,MAAM,kBAAqD,EAAE,oBAAoB;EAC/E,MAAM,OAAO,yBAAyB,cAAc,gBAAgB;EACpE,IAAI,QAAQ,OAAO,CAAC,YAAY,UAAU,CAAC,MAAM,SAAS,SAAS,IAAI,GACrE,YAAY,UAAU;EAExB,YAAY,UAAU;CACxB;CAEA,MAAM,iBAAoD,EAAE,oBAAoB;EAC9E,IAAI,yBAAyB,QAAQ,CAAC,MAAM,SAAS,SAAS,aAAa,GACzE,YAAY,UAAU;CAE1B;CAEA,MAAM,kBAAqD,EAAE,aAAa;EACxE,iBAAiB,UAAU,WAAW;CACxC;CAEA,MAAM,WAAW,YAA2C;EAC1D,IAAI,WAAW,QAAQ,CAAC,iBAAiB,WAAW,CAAC,SAAS,OAAO,GACnE;EAGF,MAAM,UADY,QAAQ,sBAAsB,QAAQ,uBAAA,EAC9B,cAAc,QAAQ,KAAK,YAAY;EACjE,IAAI,QAAQ,aACV,OAAO,MAAM;CAEjB;CAEA,OAAO;EACL,YAAY;GAAE,KAAK;GAAO;GAAgB;GAAe;EAAe;EACxE;CACF;AACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
//#region src/theme/theme-switcher.d.ts
|
|
3
|
+
type ThemeSwitcherProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
'data-component'?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const THEME_SWITCHER_NAME = "ThemeSwitcher";
|
|
8
|
+
declare function ThemeSwitcher({ className, 'data-component': componentName }: ThemeSwitcherProps): ReactElement;
|
|
9
|
+
declare namespace ThemeSwitcher {
|
|
10
|
+
export { THEME_SWITCHER_NAME as displayName };
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ThemeSwitcher, ThemeSwitcherProps };
|
|
14
|
+
//# sourceMappingURL=theme-switcher.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { themeSwitcherPhrases } from "./theme-switcher.phrases.js";
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Button, Tooltip, cn, usePhrases } from "@enonic/ui";
|
|
5
|
+
import { Moon, Sun, SunMoon } from "lucide-react";
|
|
6
|
+
import { readTheme, subscribeTheme, writeTheme } from "@enonic/ui-utils";
|
|
7
|
+
//#region src/theme/theme-switcher.tsx
|
|
8
|
+
const THEME_SWITCHER_NAME = "ThemeSwitcher";
|
|
9
|
+
const NEXT_THEME = {
|
|
10
|
+
light: "dark",
|
|
11
|
+
dark: "system",
|
|
12
|
+
system: "light"
|
|
13
|
+
};
|
|
14
|
+
const TITLE_KEYS = {
|
|
15
|
+
light: "enonic.uiKit.themeSwitcher.light",
|
|
16
|
+
dark: "enonic.uiKit.themeSwitcher.dark",
|
|
17
|
+
system: "enonic.uiKit.themeSwitcher.system"
|
|
18
|
+
};
|
|
19
|
+
const ACTION_KEYS = {
|
|
20
|
+
light: "enonic.uiKit.themeSwitcher.switchToDark",
|
|
21
|
+
dark: "enonic.uiKit.themeSwitcher.switchToSystem",
|
|
22
|
+
system: "enonic.uiKit.themeSwitcher.switchToLight"
|
|
23
|
+
};
|
|
24
|
+
const ICON_CLASS_NAME = "absolute transition-all duration-300";
|
|
25
|
+
const SHOWN_CLASS_NAME = "scale-100 rotate-0 opacity-100";
|
|
26
|
+
const HIDDEN_CLASS_NAME = "scale-0 -rotate-90 opacity-0";
|
|
27
|
+
const iconClassName = (shown) => cn(ICON_CLASS_NAME, shown ? SHOWN_CLASS_NAME : HIDDEN_CLASS_NAME);
|
|
28
|
+
/**
|
|
29
|
+
* Cycles the theme the admin applications share — light, dark, system — and shows the one picked.
|
|
30
|
+
* It reads and writes the stored theme itself, so every switcher and every page follows a click,
|
|
31
|
+
* here or in another tab; what an application paints comes from `useResolvedTheme`.
|
|
32
|
+
*/
|
|
33
|
+
const ThemeSwitcher = ({ className, "data-component": componentName = THEME_SWITCHER_NAME }) => {
|
|
34
|
+
const theme = useSyncExternalStore(subscribeTheme, readTheme);
|
|
35
|
+
const t = usePhrases(themeSwitcherPhrases);
|
|
36
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
37
|
+
value: t(TITLE_KEYS[theme]),
|
|
38
|
+
delay: 300,
|
|
39
|
+
asChild: true,
|
|
40
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
41
|
+
"data-component": componentName,
|
|
42
|
+
size: "sm",
|
|
43
|
+
variant: "text",
|
|
44
|
+
"aria-label": t(ACTION_KEYS[theme]),
|
|
45
|
+
onClick: () => writeTheme(NEXT_THEME[theme]),
|
|
46
|
+
className: cn("relative size-9 rounded-sm p-0", className),
|
|
47
|
+
children: [
|
|
48
|
+
/* @__PURE__ */ jsx(Sun, {
|
|
49
|
+
className: iconClassName(theme === "light"),
|
|
50
|
+
size: 16,
|
|
51
|
+
strokeWidth: 1.5
|
|
52
|
+
}),
|
|
53
|
+
/* @__PURE__ */ jsx(Moon, {
|
|
54
|
+
className: iconClassName(theme === "dark"),
|
|
55
|
+
size: 16,
|
|
56
|
+
strokeWidth: 1.5
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ jsx(SunMoon, {
|
|
59
|
+
className: iconClassName(theme === "system"),
|
|
60
|
+
size: 18,
|
|
61
|
+
strokeWidth: 1.5
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
ThemeSwitcher.displayName = THEME_SWITCHER_NAME;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { ThemeSwitcher };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=theme-switcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-switcher.js","names":[],"sources":["../../src/theme/theme-switcher.tsx"],"sourcesContent":["import { Button, cn, Tooltip, usePhrases } from '@enonic/ui';\nimport type { Theme } from '@enonic/ui-types';\nimport { readTheme, subscribeTheme, writeTheme } from '@enonic/ui-utils';\nimport { Moon, Sun, SunMoon } from 'lucide-react';\nimport { type ReactElement, useSyncExternalStore } from 'react';\n\nimport { type ThemeSwitcherPhraseKey, themeSwitcherPhrases } from './theme-switcher.phrases';\n\nexport type ThemeSwitcherProps = {\n className?: string;\n 'data-component'?: string;\n};\n\nconst THEME_SWITCHER_NAME = 'ThemeSwitcher';\n\nconst NEXT_THEME: Record<Theme, Theme> = { light: 'dark', dark: 'system', system: 'light' };\n\nconst TITLE_KEYS: Record<Theme, ThemeSwitcherPhraseKey> = {\n light: 'enonic.uiKit.themeSwitcher.light',\n dark: 'enonic.uiKit.themeSwitcher.dark',\n system: 'enonic.uiKit.themeSwitcher.system',\n};\n\nconst ACTION_KEYS: Record<Theme, ThemeSwitcherPhraseKey> = {\n light: 'enonic.uiKit.themeSwitcher.switchToDark',\n dark: 'enonic.uiKit.themeSwitcher.switchToSystem',\n system: 'enonic.uiKit.themeSwitcher.switchToLight',\n};\n\nconst ICON_CLASS_NAME = 'absolute transition-all duration-300';\nconst SHOWN_CLASS_NAME = 'scale-100 rotate-0 opacity-100';\nconst HIDDEN_CLASS_NAME = 'scale-0 -rotate-90 opacity-0';\n\nconst iconClassName = (shown: boolean): string =>\n cn(ICON_CLASS_NAME, shown ? SHOWN_CLASS_NAME : HIDDEN_CLASS_NAME);\n\n/**\n * Cycles the theme the admin applications share — light, dark, system — and shows the one picked.\n * It reads and writes the stored theme itself, so every switcher and every page follows a click,\n * here or in another tab; what an application paints comes from `useResolvedTheme`.\n */\nconst ThemeSwitcher = ({\n className,\n 'data-component': componentName = THEME_SWITCHER_NAME,\n}: ThemeSwitcherProps): ReactElement => {\n const theme = useSyncExternalStore(subscribeTheme, readTheme);\n const t = usePhrases(themeSwitcherPhrases);\n\n return (\n <Tooltip value={t(TITLE_KEYS[theme])} delay={300} asChild>\n <Button\n data-component={componentName}\n size=\"sm\"\n variant=\"text\"\n aria-label={t(ACTION_KEYS[theme])}\n onClick={() => writeTheme(NEXT_THEME[theme])}\n className={cn('relative size-9 rounded-sm p-0', className)}\n >\n <Sun className={iconClassName(theme === 'light')} size={16} strokeWidth={1.5} />\n <Moon className={iconClassName(theme === 'dark')} size={16} strokeWidth={1.5} />\n <SunMoon className={iconClassName(theme === 'system')} size={18} strokeWidth={1.5} />\n </Button>\n </Tooltip>\n );\n};\n\nThemeSwitcher.displayName = THEME_SWITCHER_NAME;\n\nexport { ThemeSwitcher };\n"],"mappings":";;;;;;;AAaA,MAAM,sBAAsB;AAE5B,MAAM,aAAmC;CAAE,OAAO;CAAQ,MAAM;CAAU,QAAQ;AAAQ;AAE1F,MAAM,aAAoD;CACxD,OAAO;CACP,MAAM;CACN,QAAQ;AACV;AAEA,MAAM,cAAqD;CACzD,OAAO;CACP,MAAM;CACN,QAAQ;AACV;AAEA,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAE1B,MAAM,iBAAiB,UACrB,GAAG,iBAAiB,QAAQ,mBAAmB,iBAAiB;;;;;;AAOlE,MAAM,iBAAiB,EACrB,WACA,kBAAkB,gBAAgB,0BACI;CACtC,MAAM,QAAQ,qBAAqB,gBAAgB,SAAS;CAC5D,MAAM,IAAI,WAAW,oBAAoB;CAEzC,OACE,oBAAC,SAAD;EAAS,OAAO,EAAE,WAAW,MAAM;EAAG,OAAO;EAAK,SAAA;EAChD,UAAA,qBAAC,QAAD;GACE,kBAAgB;GAChB,MAAK;GACL,SAAQ;GACR,cAAY,EAAE,YAAY,MAAM;GAChC,eAAe,WAAW,WAAW,MAAM;GAC3C,WAAW,GAAG,kCAAkC,SAAS;GAN3D,UAAA;IAQE,oBAAC,KAAD;KAAK,WAAW,cAAc,UAAU,OAAO;KAAG,MAAM;KAAI,aAAa;IAAM,CAAA;IAC/E,oBAAC,MAAD;KAAM,WAAW,cAAc,UAAU,MAAM;KAAG,MAAM;KAAI,aAAa;IAAM,CAAA;IAC/E,oBAAC,SAAD;KAAS,WAAW,cAAc,UAAU,QAAQ;KAAG,MAAM;KAAI,aAAa;IAAM,CAAA;GAC9E;;CACD,CAAA;AAEb;AAEA,cAAc,cAAc"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/theme/theme-switcher.phrases.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The switcher's own English, keyed `enonic.uiKit.themeSwitcher.<name>`: the tooltip names the
|
|
4
|
+
* theme picked, the accessible name what a click does. See `uiKitPhrases`.
|
|
5
|
+
*/
|
|
6
|
+
declare const themeSwitcherPhrases: {
|
|
7
|
+
readonly 'enonic.uiKit.themeSwitcher.light': 'Light theme';
|
|
8
|
+
readonly 'enonic.uiKit.themeSwitcher.dark': 'Dark theme';
|
|
9
|
+
readonly 'enonic.uiKit.themeSwitcher.system': 'System preference';
|
|
10
|
+
readonly 'enonic.uiKit.themeSwitcher.switchToDark': 'Switch to dark theme';
|
|
11
|
+
readonly 'enonic.uiKit.themeSwitcher.switchToSystem': 'Use system theme';
|
|
12
|
+
readonly 'enonic.uiKit.themeSwitcher.switchToLight': 'Switch to light theme';
|
|
13
|
+
};
|
|
14
|
+
type ThemeSwitcherPhraseKey = keyof typeof themeSwitcherPhrases;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ThemeSwitcherPhraseKey, themeSwitcherPhrases };
|
|
17
|
+
//# sourceMappingURL=theme-switcher.phrases.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/theme/theme-switcher.phrases.ts
|
|
2
|
+
/**
|
|
3
|
+
* The switcher's own English, keyed `enonic.uiKit.themeSwitcher.<name>`: the tooltip names the
|
|
4
|
+
* theme picked, the accessible name what a click does. See `uiKitPhrases`.
|
|
5
|
+
*/
|
|
6
|
+
const themeSwitcherPhrases = {
|
|
7
|
+
"enonic.uiKit.themeSwitcher.light": "Light theme",
|
|
8
|
+
"enonic.uiKit.themeSwitcher.dark": "Dark theme",
|
|
9
|
+
"enonic.uiKit.themeSwitcher.system": "System preference",
|
|
10
|
+
"enonic.uiKit.themeSwitcher.switchToDark": "Switch to dark theme",
|
|
11
|
+
"enonic.uiKit.themeSwitcher.switchToSystem": "Use system theme",
|
|
12
|
+
"enonic.uiKit.themeSwitcher.switchToLight": "Switch to light theme"
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { themeSwitcherPhrases };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=theme-switcher.phrases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-switcher.phrases.js","names":[],"sources":["../../src/theme/theme-switcher.phrases.ts"],"sourcesContent":["/**\n * The switcher's own English, keyed `enonic.uiKit.themeSwitcher.<name>`: the tooltip names the\n * theme picked, the accessible name what a click does. See `uiKitPhrases`.\n */\nexport const themeSwitcherPhrases = {\n 'enonic.uiKit.themeSwitcher.light': 'Light theme',\n 'enonic.uiKit.themeSwitcher.dark': 'Dark theme',\n 'enonic.uiKit.themeSwitcher.system': 'System preference',\n 'enonic.uiKit.themeSwitcher.switchToDark': 'Switch to dark theme',\n 'enonic.uiKit.themeSwitcher.switchToSystem': 'Use system theme',\n 'enonic.uiKit.themeSwitcher.switchToLight': 'Switch to light theme',\n} as const;\n\nexport type ThemeSwitcherPhraseKey = keyof typeof themeSwitcherPhrases;\n"],"mappings":";;;;;AAIA,MAAa,uBAAuB;CAClC,oCAAoC;CACpC,mCAAmC;CACnC,qCAAqC;CACrC,2CAA2C;CAC3C,6CAA6C;CAC7C,4CAA4C;AAC9C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ResolvedTheme } from "@enonic/ui-types";
|
|
2
|
+
//#region src/theme/use-resolved-theme.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The theme to paint, re-rendering when the user switches — on this page or in another tab — or
|
|
5
|
+
* the OS does while the theme is `system`. Read from storage, so there is no store to keep.
|
|
6
|
+
*/
|
|
7
|
+
declare function useResolvedTheme(): ResolvedTheme;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { useResolvedTheme };
|
|
10
|
+
//# sourceMappingURL=use-resolved-theme.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import { readResolvedTheme, subscribeResolvedTheme } from "@enonic/ui-utils";
|
|
3
|
+
//#region src/theme/use-resolved-theme.ts
|
|
4
|
+
/**
|
|
5
|
+
* The theme to paint, re-rendering when the user switches — on this page or in another tab — or
|
|
6
|
+
* the OS does while the theme is `system`. Read from storage, so there is no store to keep.
|
|
7
|
+
*/
|
|
8
|
+
function useResolvedTheme() {
|
|
9
|
+
return useSyncExternalStore(subscribeResolvedTheme, readResolvedTheme);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { useResolvedTheme };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=use-resolved-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-resolved-theme.js","names":[],"sources":["../../src/theme/use-resolved-theme.ts"],"sourcesContent":["import type { ResolvedTheme } from '@enonic/ui-types';\nimport { readResolvedTheme, subscribeResolvedTheme } from '@enonic/ui-utils';\nimport { useSyncExternalStore } from 'react';\n\n/**\n * The theme to paint, re-rendering when the user switches — on this page or in another tab — or\n * the OS does while the theme is `system`. Read from storage, so there is no store to keep.\n */\nexport function useResolvedTheme(): ResolvedTheme {\n return useSyncExternalStore(subscribeResolvedTheme, readResolvedTheme);\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,mBAAkC;CAChD,OAAO,qBAAqB,wBAAwB,iBAAiB;AACvE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enonic/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Composite React components with behaviour: the dialog shell and its confirmations, layouts, toolbars, browse screens",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|
|
@@ -41,19 +41,20 @@
|
|
|
41
41
|
"provenance": false
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@enonic/ui-
|
|
45
|
-
"
|
|
44
|
+
"@enonic/ui-types": "^0.7.0",
|
|
45
|
+
"@enonic/ui-utils": "^0.7.0",
|
|
46
|
+
"lucide-react": "^1.64.0"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
|
-
"@enonic/ui": "^1.
|
|
49
|
-
"@radix-ui/react-slot": "^1.
|
|
49
|
+
"@enonic/ui": "^1.5.0",
|
|
50
|
+
"@radix-ui/react-slot": "^1.5.0",
|
|
50
51
|
"@storybook/preact-vite": "~10.6.0",
|
|
51
|
-
"@testing-library/preact": "~3.2.
|
|
52
|
-
"@testing-library/user-event": "~14.6.
|
|
53
|
-
"@types/node": "^26.6.
|
|
52
|
+
"@testing-library/preact": "~3.2.6",
|
|
53
|
+
"@testing-library/user-event": "~14.6.8",
|
|
54
|
+
"@types/node": "^26.6.6",
|
|
54
55
|
"@types/react": "^19.2.18",
|
|
55
56
|
"@types/react-dom": "^19.2.4",
|
|
56
|
-
"happy-dom": "~
|
|
57
|
+
"happy-dom": "~21.0.4",
|
|
57
58
|
"preact": "^10.29.8",
|
|
58
59
|
"publint": "^0.3.24",
|
|
59
60
|
"typescript": "^7.0.2",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"vitest": "4.1.10"
|
|
62
63
|
},
|
|
63
64
|
"peerDependencies": {
|
|
64
|
-
"@enonic/ui": "^1.
|
|
65
|
+
"@enonic/ui": "^1.5.0",
|
|
65
66
|
"preact": ">=10.0.0",
|
|
66
67
|
"react": "^19.0.0",
|
|
67
68
|
"react-dom": "^19.0.0"
|