@vuu-ui/vuu-shell 2.1.19-beta.1 → 2.1.19-beta.2

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.
Files changed (64) hide show
  1. package/package.json +15 -16
  2. package/src/ShellContextProvider.js +20 -0
  3. package/src/app-header/AppHeader.css.js +21 -0
  4. package/src/app-header/AppHeader.js +97 -0
  5. package/src/app-header/index.js +1 -0
  6. package/src/application-provider/ApplicationContext.js +9 -0
  7. package/src/application-provider/ApplicationProvider.js +77 -0
  8. package/src/application-provider/index.js +1 -0
  9. package/src/feature/Feature.js +37 -0
  10. package/src/feature/FeatureErrorBoundary.js +33 -0
  11. package/src/feature/Loader.js +5 -0
  12. package/src/feature/index.js +1 -0
  13. package/src/feature-and-layout-provider/FeatureAndLayoutProvider.js +42 -0
  14. package/src/feature-and-layout-provider/index.js +1 -0
  15. package/src/feature-list/FeatureList.css.js +62 -0
  16. package/src/feature-list/FeatureList.js +128 -0
  17. package/src/feature-list/index.js +1 -0
  18. package/src/get-layout-history.js +7 -0
  19. package/src/index.js +14 -0
  20. package/src/left-nav/LeftNav.css.js +177 -0
  21. package/src/left-nav/LeftNav.js +193 -0
  22. package/src/left-nav/index.js +1 -0
  23. package/src/login/LoginPanel.css.js +72 -0
  24. package/src/login/LoginPanel.js +124 -0
  25. package/src/login/VuuLogo.js +108 -0
  26. package/src/persistence-manager/LocalPersistenceManager.js +156 -0
  27. package/src/persistence-manager/PersistenceManager.js +0 -0
  28. package/src/persistence-manager/PersistenceProvider.js +14 -0
  29. package/src/persistence-manager/RemotePersistenceManager.js +123 -0
  30. package/src/persistence-manager/StaticPersistenceManager.js +53 -0
  31. package/src/persistence-manager/index.js +5 -0
  32. package/src/shell-layout-templates/context-panel/ContextPanel.css.js +66 -0
  33. package/src/shell-layout-templates/context-panel/ContextPanel.js +92 -0
  34. package/src/shell-layout-templates/context-panel/index.js +1 -0
  35. package/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.js +44 -0
  36. package/src/shell-layout-templates/index.js +3 -0
  37. package/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.js +68 -0
  38. package/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.js +32 -0
  39. package/src/shell-layout-templates/side-panel/SidePanel.css.js +9 -0
  40. package/src/shell-layout-templates/side-panel/SidePanel.js +31 -0
  41. package/src/shell-layout-templates/side-panel/index.js +1 -0
  42. package/src/shell-layout-templates/simple-content-pane/useSimpleContentPane.js +38 -0
  43. package/src/shell-layout-templates/useShellLayout.js +15 -0
  44. package/src/shell.css.js +59 -0
  45. package/src/shell.js +108 -0
  46. package/src/theme-switch/ThemeSwitch.css.js +34 -0
  47. package/src/theme-switch/ThemeSwitch.js +50 -0
  48. package/src/theme-switch/index.js +1 -0
  49. package/src/user-settings/SettingsForm.css.js +10 -0
  50. package/src/user-settings/SettingsForm.js +141 -0
  51. package/src/user-settings/UserSettingsPanel.css.js +9 -0
  52. package/src/user-settings/UserSettingsPanel.js +29 -0
  53. package/src/user-settings/index.js +2 -0
  54. package/src/workspace-management/LayoutList.css.js +74 -0
  55. package/src/workspace-management/LayoutList.js +130 -0
  56. package/src/workspace-management/LayoutTile.css.js +31 -0
  57. package/src/workspace-management/LayoutTile.js +41 -0
  58. package/src/workspace-management/SaveLayoutPanel.css.js +123 -0
  59. package/src/workspace-management/SaveLayoutPanel.js +141 -0
  60. package/src/workspace-management/WorkspaceProvider.js +209 -0
  61. package/src/workspace-management/defaultWorkspaceJSON.js +88 -0
  62. package/src/workspace-management/index.js +5 -0
  63. package/src/workspace-management/screenshot-utils.js +13 -0
  64. package/src/workspace-management/useWorkspaceContextMenuItems.js +58 -0
@@ -0,0 +1,3 @@
1
+ export * from "./context-panel/index.js";
2
+ export * from "./useShellLayout.js";
3
+ export * from "./side-panel/index.js";
@@ -0,0 +1,68 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { DockLayout, Drawer, Flexbox, LayoutContainer, View } from "@vuu-ui/vuu-layout";
3
+ import { VuuShellLocation } from "@vuu-ui/vuu-utils";
4
+ import { useCallback, useMemo, useRef, useState } from "react";
5
+ const useInlayLeftPanel = ({ SidePanelProps: LeftSidePanelProps, appHeader, htmlAttributes })=>{
6
+ const paletteView = useRef(null);
7
+ const [open, setOpen] = useState(true);
8
+ const handleDrawerClick = useCallback((e)=>{
9
+ const target = e.target;
10
+ if (!paletteView.current?.contains(target)) setOpen(!open);
11
+ }, [
12
+ open
13
+ ]);
14
+ return useMemo(()=>{
15
+ const getDrawers = (leftSidePanel)=>{
16
+ const drawers = [];
17
+ drawers.push(/*#__PURE__*/ jsx(Drawer, {
18
+ onClick: handleDrawerClick,
19
+ open: open,
20
+ position: "left",
21
+ inline: true,
22
+ peekaboo: true,
23
+ sizeOpen: 200,
24
+ toggleButton: "end",
25
+ children: /*#__PURE__*/ jsx(View, {
26
+ className: "vuuShell-palette",
27
+ id: "vw-app-palette",
28
+ ref: paletteView,
29
+ style: {
30
+ height: "100%"
31
+ },
32
+ children: leftSidePanel
33
+ }, "app-palette")
34
+ }, "left-panel"));
35
+ return drawers;
36
+ };
37
+ return /*#__PURE__*/ jsxs(Flexbox, {
38
+ ...htmlAttributes,
39
+ style: {
40
+ ...htmlAttributes?.style,
41
+ flexDirection: "column"
42
+ },
43
+ children: [
44
+ appHeader,
45
+ /*#__PURE__*/ jsx(DockLayout, {
46
+ style: {
47
+ flex: 1
48
+ },
49
+ children: getDrawers(LeftSidePanelProps?.children).concat(/*#__PURE__*/ jsx(LayoutContainer, {
50
+ dropTarget: true,
51
+ id: VuuShellLocation.WorkspaceContainer,
52
+ style: {
53
+ width: "100%",
54
+ height: "100%"
55
+ }
56
+ }, "main-content"))
57
+ })
58
+ ]
59
+ });
60
+ }, [
61
+ LeftSidePanelProps,
62
+ appHeader,
63
+ handleDrawerClick,
64
+ htmlAttributes,
65
+ open
66
+ ]);
67
+ };
68
+ export { useInlayLeftPanel };
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Flexbox, StackLayout } from "@vuu-ui/vuu-layout";
3
+ import { VuuShellLocation } from "@vuu-ui/vuu-utils";
4
+ import { useMemo } from "react";
5
+ const useLeftMainTabs = ({ appHeader, htmlAttributes, ToolbarProps })=>{
6
+ if (void 0 === ToolbarProps) throw Error("LeftMainTabs layout requires ToolbarProps");
7
+ return useMemo(()=>/*#__PURE__*/ jsxs(Flexbox, {
8
+ ...htmlAttributes,
9
+ style: {
10
+ ...htmlAttributes?.style,
11
+ flexDirection: "column"
12
+ },
13
+ children: [
14
+ appHeader,
15
+ /*#__PURE__*/ jsx(StackLayout, {
16
+ TabstripProps: {
17
+ className: `${VuuShellLocation.MultiWorkspaceContainer}-tabs`
18
+ },
19
+ active: 0,
20
+ showTabs: "left",
21
+ style: {
22
+ flex: 1
23
+ },
24
+ id: VuuShellLocation.MultiWorkspaceContainer
25
+ })
26
+ ]
27
+ }), [
28
+ appHeader,
29
+ htmlAttributes
30
+ ]);
31
+ };
32
+ export { useLeftMainTabs };
@@ -0,0 +1,9 @@
1
+ const css = `
2
+ .vuuShellSidePanel {
3
+ width: var(--shell-left-nav-size);
4
+ transition: width .2s ease-out;
5
+ }
6
+
7
+
8
+ `;
9
+ export default css;
@@ -0,0 +1,31 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import clsx from "clsx";
4
+ import { useComponentCssInjection } from "@salt-ds/styles";
5
+ import { useWindow } from "@salt-ds/window";
6
+ import SidePanel from "./SidePanel.css";
7
+ const classBase = "vuuShellSidePanel";
8
+ const SidePanel_SidePanel = ({ children, open = true, sizeClosed = 90, sizeOpen = 200, style: styleProp, ...htmlAttributes })=>{
9
+ const targetWindow = useWindow();
10
+ useComponentCssInjection({
11
+ testId: "vuu-side-panel",
12
+ css: SidePanel,
13
+ window: targetWindow
14
+ });
15
+ const style = useMemo(()=>({
16
+ ...styleProp,
17
+ "--shell-left-nav-size": open ? `${sizeOpen}px` : `${sizeClosed}px`
18
+ }), [
19
+ open,
20
+ sizeClosed,
21
+ sizeOpen,
22
+ styleProp
23
+ ]);
24
+ return /*#__PURE__*/ jsx("div", {
25
+ ...htmlAttributes,
26
+ className: clsx(classBase),
27
+ style: style,
28
+ children: children
29
+ });
30
+ };
31
+ export { SidePanel_SidePanel as SidePanel };
@@ -0,0 +1 @@
1
+ export * from "./SidePanel.js";
@@ -0,0 +1,38 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Flexbox, LayoutContainer } from "@vuu-ui/vuu-layout";
3
+ import { VuuShellLocation } from "@vuu-ui/vuu-utils";
4
+ import { ContextPanel } from "../context-panel/index.js";
5
+ import { useMemo } from "react";
6
+ const useSimpleContentPane = ({ appHeader, htmlAttributes })=>useMemo(()=>/*#__PURE__*/ jsxs(Flexbox, {
7
+ ...htmlAttributes,
8
+ style: {
9
+ ...htmlAttributes?.style,
10
+ flexDirection: "row"
11
+ },
12
+ children: [
13
+ /*#__PURE__*/ jsxs(Flexbox, {
14
+ className: "vuuShell-content",
15
+ style: {
16
+ flex: 1,
17
+ flexDirection: "column"
18
+ },
19
+ children: [
20
+ appHeader,
21
+ /*#__PURE__*/ jsx(LayoutContainer, {
22
+ id: VuuShellLocation.WorkspaceContainer,
23
+ style: {
24
+ flex: 1
25
+ }
26
+ }, "main-content")
27
+ ]
28
+ }),
29
+ /*#__PURE__*/ jsx(ContextPanel, {
30
+ id: VuuShellLocation.ContextPanel,
31
+ overlay: true
32
+ })
33
+ ]
34
+ }), [
35
+ appHeader,
36
+ htmlAttributes
37
+ ]);
38
+ export { useSimpleContentPane };
@@ -0,0 +1,15 @@
1
+ import { useFullHeightLeftPanel } from "./full-height-left-panel/useFullHeightLeftPanel.js";
2
+ import { useInlayLeftPanel } from "./inlay-left-panel/useInlayLeftPanel.js";
3
+ import { useLeftMainTabs } from "./left-main-tabs/useLeftMainTabs.js";
4
+ import { useSimpleContentPane } from "./simple-content-pane/useSimpleContentPane.js";
5
+ const LayoutHook = {
6
+ "full-height": useFullHeightLeftPanel,
7
+ inlay: useInlayLeftPanel,
8
+ "left-main-tabs": useLeftMainTabs,
9
+ "simple-content-pane": useSimpleContentPane
10
+ };
11
+ const useShellLayout = ({ layoutTemplateId = "simple-content-pane", ...props })=>{
12
+ const useLayoutHook = LayoutHook[layoutTemplateId];
13
+ return useLayoutHook(props);
14
+ };
15
+ export { useShellLayout };
@@ -0,0 +1,59 @@
1
+ const css = `
2
+ .vuuShell {
3
+ background-color: var(--vuuShell-background, var(--salt-container-primary-background));
4
+ height: var(--vuuShell-height, 100vh);
5
+ width: var(--vuuShell-width, 100vw);
6
+
7
+ & .left-main-tabs {
8
+ --vuuOverflowContainer-width: 48px;
9
+ }
10
+ }
11
+
12
+ .vuu-workspace-tabs {
13
+ background: var(--vuuWorkspace-background, var(--salt-container-primary-background));
14
+ }
15
+
16
+ .vuuShell-palette {
17
+ --vuuView-border: none;
18
+ --vuuView-margin: 0;
19
+ }
20
+
21
+ .vuuShell-warningPlaceholder {
22
+ background-color: var(--salt-container-background-high);
23
+ height: 100%;
24
+ }
25
+
26
+ .vuuToolbarProxy {
27
+ background: var(--salt-container-primary-background);
28
+ }
29
+
30
+ .vuu-workspace-tabs > .vuuTabstrip > .vuuOverflowContainer-wrapContainer {
31
+ background: var(--vuuShell-background, var(--salt-container-primary-background));
32
+ }
33
+
34
+ .vuu-workspace-tabs {
35
+ --vuuTab-height: 28px;
36
+ border: solid 1px var(--salt-separable-primary-borderColor);
37
+ border-radius: 6px;
38
+ width: 100%;
39
+ height: 100%;
40
+ padding: 36px 8px 8px;
41
+ position: relative;
42
+ border-top: none !important;
43
+ }
44
+
45
+ .vuu-workspace-tabs > .vuuTabstrip {
46
+ padding-bottom: 7px;
47
+ top: 0;
48
+ left: 0;
49
+ right: 0;
50
+ position: absolute !important;
51
+ }
52
+
53
+ .vuuShell-content {
54
+ padding: var(--vuuShell-content-padding, 8px);
55
+ }
56
+
57
+
58
+ `;
59
+ export default css;
package/src/shell.js ADDED
@@ -0,0 +1,108 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useComponentCssInjection } from "@salt-ds/styles";
3
+ import { useWindow } from "@salt-ds/window";
4
+ import { ContextMenuProvider } from "@vuu-ui/vuu-context-menu";
5
+ import { LayoutProvider, StackLayout } from "@vuu-ui/vuu-layout";
6
+ import { NotificationsProvider } from "@vuu-ui/vuu-notifications";
7
+ import { ModalProvider } from "@vuu-ui/vuu-ui-controls";
8
+ import { logger, registerComponent } from "@vuu-ui/vuu-utils";
9
+ import { useCallback, useMemo } from "react";
10
+ import { AppHeader } from "./app-header/index.js";
11
+ import { ApplicationProvider } from "./application-provider/index.js";
12
+ import { LocalPersistenceManager, PersistenceProvider, usePersistenceManager } from "./persistence-manager/index.js";
13
+ import { useShellLayout } from "./shell-layout-templates/index.js";
14
+ import { UserSettingsPanel } from "./user-settings/index.js";
15
+ import { WorkspaceProvider, useWorkspace, useWorkspaceContextMenuItems } from "./workspace-management/index.js";
16
+ import { loadingJSON } from "./workspace-management/defaultWorkspaceJSON.js";
17
+ import { useLostConnection } from "@vuu-ui/vuu-data-react";
18
+ import shell from "./shell.css";
19
+ registerComponent("ApplicationSettings", UserSettingsPanel, "view");
20
+ if ("production" === process.env.NODE_ENV) {
21
+ if ("function" != typeof StackLayout) console.warn("StackLayout module not loaded, will be unable to deserialize from layout JSON");
22
+ }
23
+ const { error: error } = logger("Shell");
24
+ const defaultAppHeader = /*#__PURE__*/ jsx(AppHeader, {});
25
+ const getAppHeader = (shellLayoutProps)=>shellLayoutProps?.appHeader ?? defaultAppHeader;
26
+ const defaultHTMLAttributes = {
27
+ className: "vuuShell"
28
+ };
29
+ const getHTMLAttributes = (props)=>{
30
+ if (props?.htmlAttributes) return {
31
+ ...defaultHTMLAttributes,
32
+ ...props.htmlAttributes
33
+ };
34
+ return defaultHTMLAttributes;
35
+ };
36
+ const VuuApplication = ({ shellLayoutProps: ShellLayoutProps, children })=>{
37
+ const targetWindow = useWindow();
38
+ useComponentCssInjection({
39
+ testId: "vuu-shell",
40
+ css: shell,
41
+ window: targetWindow
42
+ });
43
+ const { workspaceJSON, saveApplicationLayout } = useWorkspace();
44
+ const { buildMenuOptions, handleMenuAction } = useWorkspaceContextMenuItems();
45
+ const handleLayoutChange = useCallback((layout)=>{
46
+ try {
47
+ saveApplicationLayout(layout);
48
+ } catch {
49
+ error?.("Failed to save layout");
50
+ }
51
+ }, [
52
+ saveApplicationLayout
53
+ ]);
54
+ const isLayoutLoading = workspaceJSON === loadingJSON;
55
+ const initialLayout = useShellLayout({
56
+ ...ShellLayoutProps,
57
+ appHeader: getAppHeader(ShellLayoutProps),
58
+ htmlAttributes: getHTMLAttributes(ShellLayoutProps)
59
+ });
60
+ useLostConnection();
61
+ return isLayoutLoading ? null : /*#__PURE__*/ jsxs(ContextMenuProvider, {
62
+ menuActionHandler: handleMenuAction,
63
+ menuBuilder: buildMenuOptions,
64
+ children: [
65
+ /*#__PURE__*/ jsx(LayoutProvider, {
66
+ workspaceJSON: workspaceJSON,
67
+ onLayoutChange: handleLayoutChange,
68
+ children: initialLayout
69
+ }),
70
+ children
71
+ ]
72
+ });
73
+ };
74
+ const Shell = ({ logout, user, userSettingsSchema, workspaceProps, ...props })=>{
75
+ const persistenceManager = usePersistenceManager();
76
+ const localPersistenceManager = useMemo(()=>{
77
+ if (persistenceManager) return;
78
+ console.log(`No Persistence Manager, configuration data will be persisted to Local Storage, key: 'vuu/${user.username}'`);
79
+ return new LocalPersistenceManager(`vuu/${user.username}`);
80
+ }, [
81
+ persistenceManager,
82
+ user.username
83
+ ]);
84
+ const shellProviders = /*#__PURE__*/ jsx(ApplicationProvider, {
85
+ density: "high",
86
+ logout: logout,
87
+ theme: "vuu-theme",
88
+ user: user,
89
+ userSettingsSchema: userSettingsSchema,
90
+ children: /*#__PURE__*/ jsx(WorkspaceProvider, {
91
+ ...workspaceProps,
92
+ children: /*#__PURE__*/ jsx(ModalProvider, {
93
+ children: /*#__PURE__*/ jsx(NotificationsProvider, {
94
+ children: /*#__PURE__*/ jsx(VuuApplication, {
95
+ ...props,
96
+ user: user
97
+ })
98
+ })
99
+ })
100
+ })
101
+ });
102
+ if (persistenceManager) return shellProviders;
103
+ return /*#__PURE__*/ jsx(PersistenceProvider, {
104
+ persistenceManager: localPersistenceManager,
105
+ children: shellProviders
106
+ });
107
+ };
108
+ export { Shell };
@@ -0,0 +1,34 @@
1
+ const css = `
2
+ .vuuThemeSwitch {
3
+ --saltButton-minWidth: 22px;
4
+ --svg-light: url("data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\"><path d=\"M24 31q2.9 0 4.95-2.05Q31 26.9 31 24q0-2.9-2.05-4.95Q26.9 17 24 17q-2.9 0-4.95 2.05Q17 21.1 17 24q0 2.9 2.05 4.95Q21.1 31 24 31Zm0 3q-4.15 0-7.075-2.925T14 24q0-4.15 2.925-7.075T24 14q4.15 0 7.075 2.925T34 24q0 4.15-2.925 7.075T24 34ZM3.5 25.5q-.65 0-1.075-.425Q2 24.65 2 24q0-.65.425-1.075Q2.85 22.5 3.5 22.5h5q.65 0 1.075.425Q10 23.35 10 24q0 .65-.425 1.075-.425.425-1.075.425Zm36 0q-.65 0-1.075-.425Q38 24.65 38 24q0-.65.425-1.075.425-.425 1.075-.425h5q.65 0 1.075.425Q46 23.35 46 24q0 .65-.425 1.075-.425.425-1.075.425ZM24 10q-.65 0-1.075-.425Q22.5 9.15 22.5 8.5v-5q0-.65.425-1.075Q23.35 2 24 2q.65 0 1.075.425.425.425.425 1.075v5q0 .65-.425 1.075Q24.65 10 24 10Zm0 36q-.65 0-1.075-.425-.425-.425-.425-1.075v-5q0-.65.425-1.075Q23.35 38 24 38q.65 0 1.075.425.425.425.425 1.075v5q0 .65-.425 1.075Q24.65 46 24 46ZM12 14.1l-2.85-2.8q-.45-.45-.425-1.075.025-.625.425-1.075.45-.45 1.075-.45t1.075.45L14.1 12q.4.45.4 1.05 0 .6-.4 1-.4.45-1.025.45-.625 0-1.075-.4Zm24.7 24.75L33.9 36q-.4-.45-.4-1.075t.45-1.025q.4-.45 1-.45t1.05.45l2.85 2.8q.45.45.425 1.075-.025.625-.425 1.075-.45.45-1.075.45t-1.075-.45ZM33.9 14.1q-.45-.45-.45-1.05 0-.6.45-1.05l2.8-2.85q.45-.45 1.075-.425.625.025 1.075.425.45.45.45 1.075t-.45 1.075L36 14.1q-.4.4-1.025.4-.625 0-1.075-.4ZM9.15 38.85q-.45-.45-.45-1.075t.45-1.075L12 33.9q.45-.45 1.05-.45.6 0 1.05.45.45.45.45 1.05 0 .6-.45 1.05l-2.8 2.85q-.45.45-1.075.425-.625-.025-1.075-.425ZM24 24Z\"/></svg>");
5
+ --svg-dark: url("data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\"><path d=\"M24 42q-7.5 0-12.75-5.25T6 24q0-7.5 5.25-12.75T24 6q.4 0 .85.025.45.025 1.15.075-1.8 1.6-2.8 3.95-1 2.35-1 4.95 0 4.5 3.15 7.65Q28.5 25.8 33 25.8q2.6 0 4.95-.925T41.9 22.3q.05.6.075.975Q42 23.65 42 24q0 7.5-5.25 12.75T24 42Zm0-3q5.45 0 9.5-3.375t5.05-7.925q-1.25.55-2.675.825Q34.45 28.8 33 28.8q-5.75 0-9.775-4.025T19.2 15q0-1.2.25-2.575.25-1.375.9-3.125-4.9 1.35-8.125 5.475Q9 18.9 9 24q0 6.25 4.375 10.625T24 39Zm-.2-14.85Z\"/></svg>");
6
+ padding: 2px;
7
+ }
8
+
9
+ .salt-density-high .vuuThemeSwitch {
10
+ --saltButton-minWidth: 16px;
11
+ --saltButton-width: 18px;
12
+ --vuuThemeSwitch-iconSize: 16px;
13
+ }
14
+
15
+ .vuuThemeSwitch [data-icon] {
16
+ --vuu-icon-size: var(--vuuThemeSwitch-iconSize, 18px);
17
+ }
18
+
19
+ .vuuThemeSwitch [data-icon="light"] {
20
+ --vuu-icon-svg: var(--svg-light);
21
+ }
22
+
23
+ .vuuThemeSwitch [data-icon="dark"] {
24
+ --vuu-icon-svg: var(--svg-dark);
25
+ }
26
+
27
+ .vuuThemeSwitch .saltToggleButton {
28
+ width: 20px;
29
+ height: 20px;
30
+ }
31
+
32
+
33
+ `;
34
+ export default css;
@@ -0,0 +1,50 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { ToggleButton, ToggleButtonGroup, useControlled } from "@salt-ds/core";
4
+ import { useComponentCssInjection } from "@salt-ds/styles";
5
+ import { useWindow } from "@salt-ds/window";
6
+ import { useCallback } from "react";
7
+ import ThemeSwitch from "./ThemeSwitch.css";
8
+ const classBase = "vuuThemeSwitch";
9
+ const ThemeSwitch_ThemeSwitch = ({ className: classNameProp, defaultMode: defaultModeProp, mode: modeProp, onChange, ...htmlAttributes })=>{
10
+ const targetWindow = useWindow();
11
+ useComponentCssInjection({
12
+ testId: "vuu-theme-switch",
13
+ css: ThemeSwitch,
14
+ window: targetWindow
15
+ });
16
+ const [mode, setMode] = useControlled({
17
+ controlled: modeProp,
18
+ default: defaultModeProp ?? "light",
19
+ name: "ThemeSwitch",
20
+ state: "mode"
21
+ });
22
+ const handleChangeSecondary = useCallback((evt)=>{
23
+ const { value } = evt.target;
24
+ setMode(value);
25
+ onChange(value);
26
+ }, [
27
+ onChange,
28
+ setMode
29
+ ]);
30
+ const className = clsx(classBase, classNameProp);
31
+ return /*#__PURE__*/ jsxs(ToggleButtonGroup, {
32
+ className: className,
33
+ ...htmlAttributes,
34
+ onChange: handleChangeSecondary,
35
+ value: mode,
36
+ children: [
37
+ /*#__PURE__*/ jsx(ToggleButton, {
38
+ "aria-label": "alert",
39
+ "data-icon": "light",
40
+ value: "light"
41
+ }),
42
+ /*#__PURE__*/ jsx(ToggleButton, {
43
+ "aria-label": "home",
44
+ "data-icon": "dark",
45
+ value: "dark"
46
+ })
47
+ ]
48
+ });
49
+ };
50
+ export { ThemeSwitch_ThemeSwitch as ThemeSwitch };
@@ -0,0 +1 @@
1
+ export * from "./ThemeSwitch.js";
@@ -0,0 +1,10 @@
1
+ const css = `
2
+ .vuuSettingsForm {
3
+ gap: var(--salt-spacing-200);
4
+ flex-direction: column;
5
+ display: flex;
6
+ }
7
+
8
+
9
+ `;
10
+ export default css;
@@ -0,0 +1,141 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { VuuInput } from "@vuu-ui/vuu-ui-controls";
3
+ import { getFieldName } from "@vuu-ui/vuu-utils";
4
+ import { Dropdown, FormField, FormFieldLabel, Option, Switch, ToggleButton, ToggleButtonGroup } from "@salt-ds/core";
5
+ import { useComponentCssInjection } from "@salt-ds/styles";
6
+ import { useWindow } from "@salt-ds/window";
7
+ import clsx from "clsx";
8
+ import { useCallback, useState } from "react";
9
+ import SettingsForm from "./SettingsForm.css";
10
+ const isOption = (value)=>"object" == typeof value && "label" in value && "label" in value;
11
+ const isBooleanProperty = (property)=>"boolean" === property.type;
12
+ const isStringOrNumber = (value)=>"string" == typeof value || "number" == typeof value;
13
+ const getValueAndLabel = (value)=>isOption(value) ? [
14
+ value.value,
15
+ value.label
16
+ ] : [
17
+ value,
18
+ value
19
+ ];
20
+ const defaultPropertyValue = {
21
+ boolean: false,
22
+ number: 0,
23
+ string: ""
24
+ };
25
+ const classBase = "vuuSettingsForm";
26
+ function FormControl({ property, changeHandler, selectHandler, inputHandler, currentValue = property.defaultValue ?? defaultPropertyValue[property.type] }) {
27
+ const [value, setValue] = useState(currentValue);
28
+ if (isBooleanProperty(property)) {
29
+ const checked = "boolean" == typeof currentValue ? currentValue : property.defaultValue ?? false;
30
+ return /*#__PURE__*/ jsx(Switch, {
31
+ checked: checked,
32
+ onChange: changeHandler
33
+ });
34
+ }
35
+ if (Array.isArray(property.values)) {
36
+ if (property.values.length <= 2) return /*#__PURE__*/ jsx(ToggleButtonGroup, {
37
+ value: currentValue,
38
+ onChange: changeHandler,
39
+ children: property.values.map((valueOrOption)=>{
40
+ const [value, label] = getValueAndLabel(valueOrOption);
41
+ return /*#__PURE__*/ jsx(ToggleButton, {
42
+ value: value,
43
+ children: label
44
+ }, value);
45
+ })
46
+ });
47
+ else if (property.values.length > 2) return /*#__PURE__*/ jsx(Dropdown, {
48
+ value: currentValue,
49
+ onSelectionChange: selectHandler,
50
+ children: property.values.map((valueOrOption)=>{
51
+ const [value, label] = getValueAndLabel(valueOrOption);
52
+ return /*#__PURE__*/ jsx(Option, {
53
+ value: label,
54
+ "data-field": property.name
55
+ }, value);
56
+ })
57
+ });
58
+ } else {
59
+ const valid = isValidInput(currentValue, property.type);
60
+ const errorMessage = getTooltipContent(property.type, valid);
61
+ return /*#__PURE__*/ jsx(VuuInput, {
62
+ errorMessage: errorMessage,
63
+ onCommit: inputHandler,
64
+ onChange: (e)=>setValue(e.target.value),
65
+ value: value
66
+ }, property.name);
67
+ }
68
+ return null;
69
+ }
70
+ const isValidInput = (value, type)=>{
71
+ if ("" === value) return;
72
+ if ("string" === type) return "success";
73
+ if ("number" === type) {
74
+ if (Number.isNaN(Number(value))) return "error";
75
+ return "success";
76
+ }
77
+ };
78
+ function getTooltipContent(type, valid) {
79
+ if ("error" !== valid) return;
80
+ if ("number" === type) return /*#__PURE__*/ jsx("p", {
81
+ children: "Field is expecting a number"
82
+ });
83
+ if ("string" === type) return /*#__PURE__*/ jsx("p", {
84
+ children: "Field is expecting a string"
85
+ });
86
+ return /*#__PURE__*/ jsx("p", {
87
+ children: "Please contact Admin for more information on expected type"
88
+ });
89
+ }
90
+ const SettingsForm_SettingsForm = ({ className, settingsSchema, settings, onSettingChanged, ...htmlAttributes })=>{
91
+ const targetWindow = useWindow();
92
+ useComponentCssInjection({
93
+ testId: "vuu-settings-form",
94
+ css: SettingsForm,
95
+ window: targetWindow
96
+ });
97
+ const changeHandler = useCallback((event)=>{
98
+ const fieldName = getFieldName(event.target);
99
+ const { checked, value } = event.target;
100
+ onSettingChanged(fieldName, checked ?? value);
101
+ }, [
102
+ onSettingChanged
103
+ ]);
104
+ const selectHandler = useCallback((event, [selected])=>{
105
+ const fieldName = getFieldName(event.target);
106
+ onSettingChanged(fieldName, selected);
107
+ }, [
108
+ onSettingChanged
109
+ ]);
110
+ const inputHandler = useCallback((event)=>{
111
+ const fieldName = getFieldName(event.target);
112
+ const { value } = event.target;
113
+ if (Number.isNaN(Number(value)) || "" == value) onSettingChanged(fieldName, value);
114
+ else {
115
+ const numValue = Number(value);
116
+ onSettingChanged(fieldName, numValue);
117
+ }
118
+ }, [
119
+ onSettingChanged
120
+ ]);
121
+ return /*#__PURE__*/ jsx("div", {
122
+ ...htmlAttributes,
123
+ className: clsx(classBase, className),
124
+ children: settingsSchema.properties.map((property)=>/*#__PURE__*/ jsxs(FormField, {
125
+ "data-field": property.name,
126
+ children: [
127
+ /*#__PURE__*/ jsx(FormFieldLabel, {
128
+ children: property.label
129
+ }),
130
+ FormControl({
131
+ property,
132
+ changeHandler,
133
+ selectHandler,
134
+ inputHandler,
135
+ currentValue: settings[property.name]
136
+ })
137
+ ]
138
+ }, property.name))
139
+ });
140
+ };
141
+ export { FormControl, SettingsForm_SettingsForm as SettingsForm, isBooleanProperty, isOption, isStringOrNumber };
@@ -0,0 +1,9 @@
1
+ const css = `
2
+ .vuuUserSettingsPanel {
3
+ height: 100%;
4
+ overflow: auto;
5
+ }
6
+
7
+
8
+ `;
9
+ export default css;
@@ -0,0 +1,29 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useComponentCssInjection } from "@salt-ds/styles";
3
+ import { useWindow } from "@salt-ds/window";
4
+ import { useApplicationSettings } from "../application-provider/index.js";
5
+ import { SettingsForm } from "./SettingsForm.js";
6
+ import clsx from "clsx";
7
+ import UserSettingsPanel from "./UserSettingsPanel.css";
8
+ const classBase = "vuuUserSettingsPanel";
9
+ const UserSettingsPanel_UserSettingsPanel = ({ ...htmlAttributes })=>{
10
+ const targetWindow = useWindow();
11
+ useComponentCssInjection({
12
+ testId: "vuu-user-settings-panel",
13
+ css: UserSettingsPanel,
14
+ window: targetWindow
15
+ });
16
+ const { onUserSettingChanged, userSettings = {}, userSettingsSchema } = useApplicationSettings();
17
+ if (userSettingsSchema) return /*#__PURE__*/ jsx("div", {
18
+ ...htmlAttributes,
19
+ className: clsx(classBase, "vuuScrollable"),
20
+ children: /*#__PURE__*/ jsx(SettingsForm, {
21
+ settings: userSettings,
22
+ settingsSchema: userSettingsSchema,
23
+ onSettingChanged: onUserSettingChanged
24
+ })
25
+ });
26
+ console.log("no settingsSchema provided to UserSettingsPanel");
27
+ return null;
28
+ };
29
+ export { UserSettingsPanel_UserSettingsPanel as UserSettingsPanel };
@@ -0,0 +1,2 @@
1
+ export * from "./UserSettingsPanel.js";
2
+ export * from "./SettingsForm.js";