@valentinkolb/cloud 0.4.0 → 0.5.1
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/package.json +18 -6
- package/scripts/preload.ts +78 -23
- package/src/_internal/define-app.ts +53 -46
- package/src/api/accounts-entities.ts +4 -0
- package/src/api/admin-core-settings.ts +98 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/auth/schemas.ts +24 -0
- package/src/api/auth.ts +116 -13
- package/src/api/index.ts +7 -2
- package/src/api/me.ts +203 -14
- package/src/api/search/schemas.ts +1 -0
- package/src/api/search.ts +62 -8
- package/src/config/ssr.ts +2 -9
- package/src/contracts/announcements.test.ts +37 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app.ts +2 -0
- package/src/contracts/index.ts +3 -2
- package/src/contracts/registry.ts +2 -0
- package/src/contracts/shared.ts +108 -1
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/server/api/index.ts +1 -1
- package/src/server/api/respond.ts +50 -10
- package/src/server/index.ts +44 -38
- package/src/server/middleware/auth.ts +98 -9
- package/src/server/middleware/index.ts +2 -1
- package/src/server/middleware/settings.ts +26 -0
- package/src/server/services/access.test.ts +197 -0
- package/src/server/services/access.ts +254 -6
- package/src/server/services/index.ts +14 -11
- package/src/server/services/pagination.ts +22 -0
- package/src/server/time.ts +45 -0
- package/src/services/account-lifecycle/index.ts +142 -18
- package/src/services/accounts/app.ts +658 -170
- package/src/services/accounts/authz.test.ts +77 -0
- package/src/services/accounts/authz.ts +22 -0
- package/src/services/accounts/entities.ts +84 -5
- package/src/services/accounts/groups.ts +30 -24
- package/src/services/accounts/model.test.ts +30 -0
- package/src/services/accounts/switching.test.ts +14 -0
- package/src/services/accounts/switching.ts +15 -6
- package/src/services/accounts/users.ts +75 -52
- package/src/services/announcements/index.test.ts +32 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/audit/index.test.ts +84 -0
- package/src/services/audit/index.ts +431 -0
- package/src/services/auth-flows/index.ts +9 -2
- package/src/services/auth-flows/ipa.ts +47 -7
- package/src/services/auth-flows/magic-link.ts +92 -20
- package/src/services/auth-flows/password-reset.ts +284 -0
- package/src/services/auth-flows/proxy-return.test.ts +24 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/gateway.ts +162 -0
- package/src/services/index.ts +44 -2
- package/src/services/ipa/effective-groups.test.ts +33 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/profile.ts +45 -3
- package/src/services/ipa/search.ts +3 -5
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.test.ts +32 -0
- package/src/services/ipa/sync-planning.ts +22 -0
- package/src/services/ipa/sync.ts +110 -38
- package/src/services/notifications/index.ts +82 -11
- package/src/services/oauth-tokens.ts +104 -0
- package/src/services/postgres.ts +21 -6
- package/src/services/providers/local/auth.test.ts +22 -0
- package/src/services/providers/local/auth.ts +46 -3
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.test.ts +210 -0
- package/src/services/service-account-credentials.ts +715 -0
- package/src/services/service-accounts.ts +188 -0
- package/src/services/session/index.ts +7 -8
- package/src/services/settings/app.ts +4 -20
- package/src/services/settings/defaults.ts +79 -22
- package/src/services/settings/store.ts +47 -0
- package/src/services/weather/forecast.ts +40 -7
- package/src/services/webauthn.test.ts +36 -0
- package/src/services/webauthn.ts +384 -0
- package/src/shared/icons.ts +391 -100
- package/src/shared/index.ts +7 -0
- package/src/shared/markdown/extensions/code.ts +38 -1
- package/src/shared/markdown/extensions/images.ts +39 -3
- package/src/shared/markdown/extensions/info-blocks.ts +5 -5
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +79 -58
- package/src/shared/markdown/formula.test.ts +1089 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +76 -2
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/redirect.test.ts +58 -0
- package/src/shared/redirect.ts +56 -0
- package/src/shared/theme.test.ts +24 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +7 -3
- package/src/ssr/AdminSidebar.tsx +115 -49
- package/src/ssr/AppLaunchpad.island.tsx +176 -0
- package/src/ssr/Footer.island.tsx +3 -8
- package/src/ssr/GlobalAnnouncements.island.tsx +141 -0
- package/src/ssr/GlobalSearchDialog.tsx +545 -117
- package/src/ssr/HotkeysHelpRail.island.tsx +3 -70
- package/src/ssr/Layout.tsx +74 -66
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutHelp.tsx +266 -0
- package/src/ssr/NavMenu.island.tsx +0 -39
- package/src/ssr/ThemeToggleRail.island.tsx +3 -3
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/islands/index.ts +13 -0
- package/src/styles/base-popover.css +5 -2
- package/src/styles/effects.css +87 -6
- package/src/styles/global.css +146 -9
- package/src/styles/input.css +3 -1
- package/src/styles/utilities-buttons.css +133 -27
- package/src/styles/utilities-code-display.css +67 -0
- package/src/styles/utilities-completion.css +223 -0
- package/src/styles/utilities-detail.css +73 -0
- package/src/styles/utilities-feedback.css +16 -15
- package/src/styles/utilities-layout.css +42 -2
- package/src/styles/utilities-markdown-editor.css +472 -0
- package/src/styles/utilities-navigation.css +63 -8
- package/src/styles/utilities-script.css +84 -0
- package/src/styles/utilities-table-tile.css +229 -0
- package/src/types/ambient.d.ts +9 -0
- package/src/ui/completion/behaviors.test.ts +95 -0
- package/src/ui/completion/behaviors.ts +205 -0
- package/src/ui/completion/engine.ts +368 -0
- package/src/ui/completion/index.ts +40 -0
- package/src/ui/completion/overlay.ts +92 -0
- package/src/ui/dialog-core.ts +173 -45
- package/src/ui/filter/FilterChip.tsx +42 -40
- package/src/ui/index.ts +11 -12
- package/src/ui/input/AutocompleteEditor.tsx +656 -0
- package/src/ui/input/CheckboxCard.tsx +91 -0
- package/src/ui/input/Combobox.tsx +375 -0
- package/src/ui/input/DatePicker.tsx +846 -0
- package/src/ui/input/DateTimeInput.tsx +29 -4
- package/src/ui/input/FileDropzone.tsx +116 -0
- package/src/ui/input/IconInput.tsx +116 -0
- package/src/ui/input/ImageInput.tsx +19 -2
- package/src/ui/input/MultiSelectInput.tsx +448 -0
- package/src/ui/input/NumberInput.tsx +417 -61
- package/src/ui/input/SegmentedControl.tsx +2 -2
- package/src/ui/input/Select.tsx +172 -10
- package/src/ui/input/Slider.tsx +3 -4
- package/src/ui/input/Switch.tsx +3 -2
- package/src/ui/input/TemplateEditor.tsx +212 -0
- package/src/ui/input/TextInput.tsx +144 -13
- package/src/ui/input/index.ts +53 -8
- package/src/ui/input/markdown/MarkdownEditor.tsx +774 -0
- package/src/ui/input/markdown/Toolbar.tsx +90 -0
- package/src/ui/input/markdown/actions.ts +233 -0
- package/src/ui/input/markdown/active-formats.ts +94 -0
- package/src/ui/input/markdown/behaviors.ts +193 -0
- package/src/ui/input/markdown/code-zone.ts +23 -0
- package/src/ui/input/markdown/highlight.ts +316 -0
- package/src/ui/layout.ts +22 -0
- package/src/ui/misc/AppOverview.tsx +105 -0
- package/src/ui/misc/AppWorkspace.tsx +607 -0
- package/src/ui/misc/Calendar.tsx +1291 -0
- package/src/ui/misc/Chart.tsx +162 -0
- package/src/ui/misc/CodeDisplay.tsx +54 -0
- package/src/ui/misc/ContextMenu.tsx +2 -2
- package/src/ui/misc/DataTable.tsx +269 -0
- package/src/ui/misc/DockWorkspace.tsx +425 -0
- package/src/ui/misc/Docs.tsx +153 -0
- package/src/ui/misc/Dropdown.tsx +2 -2
- package/src/ui/misc/EntitySearch.tsx +260 -129
- package/src/ui/misc/LinkCard.tsx +14 -2
- package/src/ui/misc/LogEntriesTable.tsx +34 -31
- package/src/ui/misc/Pagination.tsx +31 -12
- package/src/ui/misc/PanelDialog.tsx +109 -0
- package/src/ui/misc/Panes.tsx +873 -0
- package/src/ui/misc/PermissionEditor.tsx +358 -262
- package/src/ui/misc/Placeholder.tsx +40 -0
- package/src/ui/misc/ProgressBar.tsx +1 -1
- package/src/ui/misc/ResourceApiKeys.tsx +260 -0
- package/src/ui/misc/SettingsModal.tsx +150 -0
- package/src/ui/misc/StatCell.tsx +182 -40
- package/src/ui/misc/StatGrid.tsx +149 -0
- package/src/ui/misc/StructuredDataPreview.tsx +107 -0
- package/src/ui/misc/code-highlight.ts +213 -0
- package/src/ui/misc/index.ts +93 -12
- package/src/ui/prompts.tsx +362 -312
- package/src/ui/toast.ts +384 -0
- package/src/ui/widgets/Widget.tsx +12 -4
- package/src/ssr/MoreAppsDropdown.island.tsx +0 -61
- package/src/ui/ipa/GroupView.tsx +0 -36
- package/src/ui/ipa/LoginBtn.tsx +0 -16
- package/src/ui/ipa/UserView.tsx +0 -58
- package/src/ui/ipa/index.ts +0 -4
- package/src/ui/navigation.ts +0 -32
- package/src/ui/sidebar.tsx +0 -468
- /package/src/ui/{ipa → misc}/Avatar.tsx +0 -0
package/src/ui/sidebar.tsx
DELETED
|
@@ -1,468 +0,0 @@
|
|
|
1
|
-
import { For, Show, createMemo, type JSX } from "solid-js";
|
|
2
|
-
|
|
3
|
-
export type SidebarRow = {
|
|
4
|
-
id: string;
|
|
5
|
-
label?: string;
|
|
6
|
-
href?: string;
|
|
7
|
-
icon?: string;
|
|
8
|
-
labelIcon?: string;
|
|
9
|
-
meta?: string;
|
|
10
|
-
active?: boolean;
|
|
11
|
-
class?: string;
|
|
12
|
-
content?: JSX.Element;
|
|
13
|
-
actionIcon?: string;
|
|
14
|
-
actionLabel?: string;
|
|
15
|
-
onActionClick?: (event: MouseEvent) => void;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type SidebarSection = {
|
|
19
|
-
title?: string;
|
|
20
|
-
rows: SidebarRow[];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type SidebarTreeNode = {
|
|
24
|
-
id: string;
|
|
25
|
-
label: string;
|
|
26
|
-
icon?: string;
|
|
27
|
-
labelIcon?: string;
|
|
28
|
-
meta?: string;
|
|
29
|
-
active?: boolean;
|
|
30
|
-
href?: string;
|
|
31
|
-
actionIcon?: string;
|
|
32
|
-
actionLabel?: string;
|
|
33
|
-
onActionClick?: (event: MouseEvent, nodeId: string) => void;
|
|
34
|
-
children?: SidebarTreeNode[];
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type SidebarTreeSpec = {
|
|
38
|
-
title?: string;
|
|
39
|
-
nodes: SidebarTreeNode[];
|
|
40
|
-
selectedId?: string;
|
|
41
|
-
expandedIds?: string[];
|
|
42
|
-
onToggle?: (nodeId: string) => void;
|
|
43
|
-
onSelect?: (nodeId: string) => void;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export type SidebarSpec = {
|
|
47
|
-
header: {
|
|
48
|
-
title: string;
|
|
49
|
-
subtitle?: string;
|
|
50
|
-
icon?: string | JSX.Element;
|
|
51
|
-
settingsHref?: string;
|
|
52
|
-
};
|
|
53
|
-
actions?: SidebarSection[] | SidebarRow[];
|
|
54
|
-
nav?: SidebarSection[] | SidebarRow[];
|
|
55
|
-
tree?: SidebarTreeSpec;
|
|
56
|
-
controls?: JSX.Element;
|
|
57
|
-
footer?: SidebarSection[] | SidebarRow[];
|
|
58
|
-
mobile?: {
|
|
59
|
-
mode?: "auto" | "hidden";
|
|
60
|
-
defaultOpen?: boolean;
|
|
61
|
-
toggleIcon?: "chevron" | "eye";
|
|
62
|
-
include?: Array<"settings" | "actions" | "nav" | "tree" | "controls" | "footer">;
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
type SidebarLayoutProps = {
|
|
67
|
-
render?: "both" | "mobile" | "desktop";
|
|
68
|
-
mobile?: {
|
|
69
|
-
header: JSX.Element;
|
|
70
|
-
items?: JSX.Element;
|
|
71
|
-
body?: JSX.Element;
|
|
72
|
-
defaultOpen?: boolean;
|
|
73
|
-
bodyClass?: string;
|
|
74
|
-
toggleIcon?: "chevron" | "eye";
|
|
75
|
-
};
|
|
76
|
-
desktop: {
|
|
77
|
-
class?: string;
|
|
78
|
-
header: JSX.Element;
|
|
79
|
-
actions?: JSX.Element;
|
|
80
|
-
body?: JSX.Element;
|
|
81
|
-
footer?: JSX.Element;
|
|
82
|
-
};
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
type SidebarFromSpecProps = {
|
|
86
|
-
spec: SidebarSpec;
|
|
87
|
-
render?: "both" | "mobile" | "desktop";
|
|
88
|
-
desktopClass?: string;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const normalizeSections = (sections?: SidebarSection[] | SidebarRow[], title?: string): SidebarSection[] => {
|
|
92
|
-
if (!sections || sections.length === 0) return [];
|
|
93
|
-
if ("rows" in sections[0]!) {
|
|
94
|
-
return sections as SidebarSection[];
|
|
95
|
-
}
|
|
96
|
-
return [{ title, rows: sections as SidebarRow[] }];
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
function SidebarRowItem(props: { row: SidebarRow; mobile?: boolean }) {
|
|
100
|
-
if (props.row.content) return <>{props.row.content}</>;
|
|
101
|
-
|
|
102
|
-
const sharedLabel = (
|
|
103
|
-
<>
|
|
104
|
-
<Show when={props.row.icon}>
|
|
105
|
-
<i class={`ti ${props.row.icon} text-sm`} />
|
|
106
|
-
</Show>
|
|
107
|
-
<div class="min-w-0 flex-1 text-left">
|
|
108
|
-
<span class="block truncate">{props.row.label}</span>
|
|
109
|
-
<Show when={props.row.meta}>
|
|
110
|
-
<span class="sidebar-item-meta block truncate">{props.row.meta}</span>
|
|
111
|
-
</Show>
|
|
112
|
-
</div>
|
|
113
|
-
<Show when={props.row.labelIcon}>
|
|
114
|
-
<i class={`ti ${props.row.labelIcon} text-xs text-dimmed`} />
|
|
115
|
-
</Show>
|
|
116
|
-
<Show when={props.row.actionIcon && !props.mobile}>
|
|
117
|
-
<button
|
|
118
|
-
type="button"
|
|
119
|
-
class="sidebar-item-action"
|
|
120
|
-
aria-label={props.row.actionLabel ?? "Row action"}
|
|
121
|
-
onClick={(event) => {
|
|
122
|
-
event.preventDefault();
|
|
123
|
-
event.stopPropagation();
|
|
124
|
-
props.row.onActionClick?.(event);
|
|
125
|
-
}}
|
|
126
|
-
>
|
|
127
|
-
<i class={`ti ${props.row.actionIcon}`} />
|
|
128
|
-
</button>
|
|
129
|
-
</Show>
|
|
130
|
-
</>
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if (props.mobile) {
|
|
134
|
-
if (props.row.href) {
|
|
135
|
-
return (
|
|
136
|
-
<a
|
|
137
|
-
href={props.row.href}
|
|
138
|
-
class={`btn-input btn-input-sm ${props.row.active ? "border-blue-500/35 bg-blue-50/70 text-blue-700 dark:border-blue-400/40 dark:bg-blue-950/40 dark:text-blue-200" : ""} ${props.row.class ?? ""}`}
|
|
139
|
-
data-row={props.row.id}
|
|
140
|
-
>
|
|
141
|
-
<Show when={props.row.icon}>
|
|
142
|
-
<i class={`ti ${props.row.icon}`} />
|
|
143
|
-
</Show>
|
|
144
|
-
<span>{props.row.label}</span>
|
|
145
|
-
</a>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
return (
|
|
149
|
-
<button
|
|
150
|
-
type="button"
|
|
151
|
-
class={`btn-input btn-input-sm ${props.row.active ? "border-blue-500/35 bg-blue-50/70 text-blue-700 dark:border-blue-400/40 dark:bg-blue-950/40 dark:text-blue-200" : ""} ${props.row.class ?? ""}`}
|
|
152
|
-
data-row={props.row.id}
|
|
153
|
-
>
|
|
154
|
-
<Show when={props.row.icon}>
|
|
155
|
-
<i class={`ti ${props.row.icon}`} />
|
|
156
|
-
</Show>
|
|
157
|
-
<span>{props.row.label}</span>
|
|
158
|
-
</button>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (props.row.href) {
|
|
163
|
-
return (
|
|
164
|
-
<a href={props.row.href} class={`sidebar-item ${props.row.active ? "sidebar-item-active" : ""} ${props.row.class ?? ""}`} data-row={props.row.id}>
|
|
165
|
-
{sharedLabel}
|
|
166
|
-
</a>
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return (
|
|
171
|
-
<button
|
|
172
|
-
type="button"
|
|
173
|
-
class={`sidebar-item ${props.row.active ? "sidebar-item-active" : ""} ${props.row.class ?? ""}`}
|
|
174
|
-
data-row={props.row.id}
|
|
175
|
-
>
|
|
176
|
-
{sharedLabel}
|
|
177
|
-
</button>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function SidebarTree(props: { tree: SidebarTreeSpec; level?: number }) {
|
|
182
|
-
const level = props.level ?? 0;
|
|
183
|
-
const expanded = createMemo(() => new Set(props.tree.expandedIds ?? []));
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<div class="sidebar-tree" role={level === 0 ? "tree" : undefined}>
|
|
187
|
-
<For each={props.tree.nodes}>
|
|
188
|
-
{(node) => {
|
|
189
|
-
const hasChildren = () => (node.children?.length ?? 0) > 0;
|
|
190
|
-
const isExpanded = () => expanded().has(node.id);
|
|
191
|
-
const isSelected = () => node.active || props.tree.selectedId === node.id;
|
|
192
|
-
const showLeafIcon = () => !hasChildren() && !!node.icon;
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
<div class="sidebar-tree-item" role="treeitem" aria-level={level + 1} aria-expanded={hasChildren() ? isExpanded() : undefined}>
|
|
196
|
-
<div class={`sidebar-tree-row ${isSelected() ? "sidebar-item-active" : ""}`} style={`--sidebar-level:${level}`}>
|
|
197
|
-
<button
|
|
198
|
-
type="button"
|
|
199
|
-
class="sidebar-tree-toggle"
|
|
200
|
-
onClick={() => {
|
|
201
|
-
if (!hasChildren()) return;
|
|
202
|
-
props.tree.onToggle?.(node.id);
|
|
203
|
-
}}
|
|
204
|
-
aria-label={hasChildren() ? (isExpanded() ? "Collapse" : "Expand") : undefined}
|
|
205
|
-
>
|
|
206
|
-
<Show when={hasChildren()} fallback={showLeafIcon() ? <i class={`ti ${node.icon} text-xs`} /> : <span class="h-2 w-2 rounded-full bg-zinc-300 dark:bg-zinc-600" />}>
|
|
207
|
-
<i class={`ti ${isExpanded() ? "ti-chevron-down" : "ti-chevron-right"} text-[10px]`} />
|
|
208
|
-
</Show>
|
|
209
|
-
</button>
|
|
210
|
-
<Show when={hasChildren() && node.icon}>
|
|
211
|
-
<i class={`ti ${node.icon} text-xs text-dimmed`} />
|
|
212
|
-
</Show>
|
|
213
|
-
<Show
|
|
214
|
-
when={node.href}
|
|
215
|
-
fallback={
|
|
216
|
-
<button type="button" class="min-w-0 flex-1 truncate text-left" onClick={() => props.tree.onSelect?.(node.id)}>
|
|
217
|
-
{node.label}
|
|
218
|
-
</button>
|
|
219
|
-
}
|
|
220
|
-
>
|
|
221
|
-
<a href={node.href!} class="min-w-0 flex-1 truncate">
|
|
222
|
-
{node.label}
|
|
223
|
-
</a>
|
|
224
|
-
</Show>
|
|
225
|
-
<Show when={node.labelIcon}>
|
|
226
|
-
<i class={`ti ${node.labelIcon} text-xs text-dimmed`} />
|
|
227
|
-
</Show>
|
|
228
|
-
<Show when={node.actionIcon}>
|
|
229
|
-
<button
|
|
230
|
-
type="button"
|
|
231
|
-
class="sidebar-item-action"
|
|
232
|
-
aria-label={node.actionLabel ?? "Row action"}
|
|
233
|
-
onClick={(event) => {
|
|
234
|
-
event.preventDefault();
|
|
235
|
-
event.stopPropagation();
|
|
236
|
-
node.onActionClick?.(event, node.id);
|
|
237
|
-
}}
|
|
238
|
-
>
|
|
239
|
-
<i class={`ti ${node.actionIcon}`} />
|
|
240
|
-
</button>
|
|
241
|
-
</Show>
|
|
242
|
-
</div>
|
|
243
|
-
<Show when={hasChildren() && isExpanded()}>
|
|
244
|
-
<div class="sidebar-tree-children">
|
|
245
|
-
<SidebarTree tree={{ ...props.tree, nodes: node.children ?? [] }} level={level + 1} />
|
|
246
|
-
</div>
|
|
247
|
-
</Show>
|
|
248
|
-
</div>
|
|
249
|
-
);
|
|
250
|
-
}}
|
|
251
|
-
</For>
|
|
252
|
-
</div>
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export function SidebarFromSpec(props: SidebarFromSpecProps) {
|
|
257
|
-
const include = createMemo(() => new Set(props.spec.mobile?.include ?? ["settings", "actions", "nav", "tree", "controls", "footer"]));
|
|
258
|
-
const mobileMode = props.spec.mobile?.mode ?? "auto";
|
|
259
|
-
|
|
260
|
-
const actionSections = createMemo(() => normalizeSections(props.spec.actions, "Actions"));
|
|
261
|
-
const navSections = createMemo(() => normalizeSections(props.spec.nav, "Navigation"));
|
|
262
|
-
const footerSections = createMemo(() => normalizeSections(props.spec.footer));
|
|
263
|
-
|
|
264
|
-
const desktopHeader = (
|
|
265
|
-
<>
|
|
266
|
-
<Show when={typeof props.spec.header.icon === "string"}>
|
|
267
|
-
<div class="w-6 h-6 rounded bg-blue-500 flex items-center justify-center text-white shrink-0">
|
|
268
|
-
<i class={`ti ${(props.spec.header.icon as string) || "ti-layout-sidebar"} text-xs`} />
|
|
269
|
-
</div>
|
|
270
|
-
</Show>
|
|
271
|
-
<Show when={typeof props.spec.header.icon !== "string" && props.spec.header.icon}>{props.spec.header.icon as JSX.Element}</Show>
|
|
272
|
-
<div class="min-w-0 flex-1">
|
|
273
|
-
<p class="truncate text-sm font-semibold text-primary">{props.spec.header.title}</p>
|
|
274
|
-
<Show when={props.spec.header.subtitle}>
|
|
275
|
-
<p class="text-xs text-dimmed truncate">{props.spec.header.subtitle}</p>
|
|
276
|
-
</Show>
|
|
277
|
-
</div>
|
|
278
|
-
<Show when={props.spec.header.settingsHref}>
|
|
279
|
-
<a href={props.spec.header.settingsHref!} class="p-0.5 text-dimmed hover:text-primary transition-colors shrink-0" title="Settings">
|
|
280
|
-
<i class="ti ti-settings text-xs" />
|
|
281
|
-
</a>
|
|
282
|
-
</Show>
|
|
283
|
-
</>
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
const desktopActions = (
|
|
287
|
-
<>
|
|
288
|
-
<For each={actionSections()}>
|
|
289
|
-
{(section) => (
|
|
290
|
-
<section class="sidebar-section">
|
|
291
|
-
<Show when={section.title}>
|
|
292
|
-
<p class="sidebar-section-title">{section.title}</p>
|
|
293
|
-
</Show>
|
|
294
|
-
<div class="flex flex-col gap-1">
|
|
295
|
-
<For each={section.rows}>{(row) => <SidebarRowItem row={row} />}</For>
|
|
296
|
-
</div>
|
|
297
|
-
</section>
|
|
298
|
-
)}
|
|
299
|
-
</For>
|
|
300
|
-
<For each={navSections()}>
|
|
301
|
-
{(section) => (
|
|
302
|
-
<section class="sidebar-section">
|
|
303
|
-
<Show when={section.title}>
|
|
304
|
-
<p class="sidebar-section-title">{section.title}</p>
|
|
305
|
-
</Show>
|
|
306
|
-
<div class="flex flex-col gap-1">
|
|
307
|
-
<For each={section.rows}>{(row) => <SidebarRowItem row={row} />}</For>
|
|
308
|
-
</div>
|
|
309
|
-
</section>
|
|
310
|
-
)}
|
|
311
|
-
</For>
|
|
312
|
-
</>
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
const desktopBody = (
|
|
316
|
-
<>
|
|
317
|
-
<Show when={props.spec.tree}>
|
|
318
|
-
<section class="sidebar-section">
|
|
319
|
-
<Show when={props.spec.tree?.title}>
|
|
320
|
-
<p class="sidebar-section-title">{props.spec.tree?.title}</p>
|
|
321
|
-
</Show>
|
|
322
|
-
<SidebarTree tree={props.spec.tree!} />
|
|
323
|
-
</section>
|
|
324
|
-
</Show>
|
|
325
|
-
<Show when={props.spec.controls}>
|
|
326
|
-
<section class="sidebar-section">{props.spec.controls}</section>
|
|
327
|
-
</Show>
|
|
328
|
-
</>
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
const desktopFooter = (
|
|
332
|
-
<For each={footerSections()}>
|
|
333
|
-
{(section) => (
|
|
334
|
-
<section class="sidebar-section">
|
|
335
|
-
<Show when={section.title}>
|
|
336
|
-
<p class="sidebar-section-title">{section.title}</p>
|
|
337
|
-
</Show>
|
|
338
|
-
<div class="flex flex-col gap-1">
|
|
339
|
-
<For each={section.rows}>{(row) => <SidebarRowItem row={row} />}</For>
|
|
340
|
-
</div>
|
|
341
|
-
</section>
|
|
342
|
-
)}
|
|
343
|
-
</For>
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
const mobileHeader = (
|
|
347
|
-
<>
|
|
348
|
-
<Show when={typeof props.spec.header.icon === "string"}>
|
|
349
|
-
<div class="w-8 h-8 rounded-lg bg-blue-500 flex items-center justify-center text-white shrink-0">
|
|
350
|
-
<i class={`ti ${(props.spec.header.icon as string) || "ti-layout-sidebar"} text-sm`} />
|
|
351
|
-
</div>
|
|
352
|
-
</Show>
|
|
353
|
-
<Show when={typeof props.spec.header.icon !== "string" && props.spec.header.icon}>{props.spec.header.icon as JSX.Element}</Show>
|
|
354
|
-
<span class="font-semibold truncate flex-1">{props.spec.header.title}</span>
|
|
355
|
-
</>
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
const mobileItems = (
|
|
359
|
-
<>
|
|
360
|
-
<Show when={include().has("settings") && props.spec.header.settingsHref}>
|
|
361
|
-
<a href={props.spec.header.settingsHref!} class="btn-input btn-input-sm">
|
|
362
|
-
<i class="ti ti-settings" />
|
|
363
|
-
Settings
|
|
364
|
-
</a>
|
|
365
|
-
</Show>
|
|
366
|
-
<Show when={include().has("actions")}>
|
|
367
|
-
<For each={actionSections()}>
|
|
368
|
-
{(section) => <For each={section.rows}>{(row) => <SidebarRowItem row={row} mobile />}</For>}
|
|
369
|
-
</For>
|
|
370
|
-
</Show>
|
|
371
|
-
<Show when={include().has("nav")}>
|
|
372
|
-
<For each={navSections()}>
|
|
373
|
-
{(section) => <For each={section.rows}>{(row) => <SidebarRowItem row={row} mobile />}</For>}
|
|
374
|
-
</For>
|
|
375
|
-
</Show>
|
|
376
|
-
<Show when={include().has("footer")}>
|
|
377
|
-
<For each={footerSections()}>
|
|
378
|
-
{(section) => <For each={section.rows}>{(row) => <SidebarRowItem row={row} mobile />}</For>}
|
|
379
|
-
</For>
|
|
380
|
-
</Show>
|
|
381
|
-
</>
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
const mobileBody = (
|
|
385
|
-
<>
|
|
386
|
-
<Show when={include().has("tree") && props.spec.tree}>
|
|
387
|
-
<section class="sidebar-section">
|
|
388
|
-
<Show when={props.spec.tree?.title}>
|
|
389
|
-
<p class="sidebar-section-title">{props.spec.tree?.title}</p>
|
|
390
|
-
</Show>
|
|
391
|
-
<SidebarTree tree={props.spec.tree!} />
|
|
392
|
-
</section>
|
|
393
|
-
</Show>
|
|
394
|
-
<Show when={include().has("controls") && props.spec.controls}>
|
|
395
|
-
<section class="sidebar-section">{props.spec.controls}</section>
|
|
396
|
-
</Show>
|
|
397
|
-
</>
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
return (
|
|
401
|
-
<SidebarLayout
|
|
402
|
-
render={props.render}
|
|
403
|
-
desktop={{
|
|
404
|
-
class: props.desktopClass,
|
|
405
|
-
header: desktopHeader,
|
|
406
|
-
actions: desktopActions,
|
|
407
|
-
body: desktopBody,
|
|
408
|
-
footer: desktopFooter,
|
|
409
|
-
}}
|
|
410
|
-
mobile={
|
|
411
|
-
mobileMode === "hidden"
|
|
412
|
-
? undefined
|
|
413
|
-
: {
|
|
414
|
-
defaultOpen: props.spec.mobile?.defaultOpen,
|
|
415
|
-
toggleIcon: props.spec.mobile?.toggleIcon,
|
|
416
|
-
header: mobileHeader,
|
|
417
|
-
items: mobileItems,
|
|
418
|
-
body: mobileBody,
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/>
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
export function SidebarLayout(props: SidebarLayoutProps) {
|
|
426
|
-
const renderMode = props.render ?? "both";
|
|
427
|
-
const mobileOpenProps = props.mobile?.defaultOpen ? { open: true } : {};
|
|
428
|
-
|
|
429
|
-
return (
|
|
430
|
-
<>
|
|
431
|
-
<Show when={(renderMode === "both" || renderMode === "mobile") && props.mobile}>
|
|
432
|
-
<nav class="lg:hidden flex flex-col gap-3">
|
|
433
|
-
<details class="group" {...mobileOpenProps}>
|
|
434
|
-
<summary class="sidebar-header cursor-pointer select-none list-none">
|
|
435
|
-
{props.mobile!.header}
|
|
436
|
-
<span class="ml-auto inline-flex h-7 w-7 items-center justify-center rounded-md text-dimmed transition-transform group-open:rotate-180">
|
|
437
|
-
<i class={`ti ${props.mobile?.toggleIcon === "eye" ? "ti-eye" : "ti-chevron-down"} text-sm`} />
|
|
438
|
-
</span>
|
|
439
|
-
</summary>
|
|
440
|
-
<Show when={props.mobile?.items}>
|
|
441
|
-
<div class="mt-2 flex flex-wrap gap-2">{props.mobile?.items}</div>
|
|
442
|
-
</Show>
|
|
443
|
-
<Show when={props.mobile?.body}>
|
|
444
|
-
<div class={`mt-2 ${props.mobile?.bodyClass ?? "max-h-64 overflow-y-auto p-2"}`}>{props.mobile?.body}</div>
|
|
445
|
-
</Show>
|
|
446
|
-
</details>
|
|
447
|
-
</nav>
|
|
448
|
-
</Show>
|
|
449
|
-
|
|
450
|
-
<Show when={renderMode === "both" || renderMode === "desktop"}>
|
|
451
|
-
<aside class={`hidden lg:flex flex-col min-h-0 overflow-y-auto ${props.desktop.class ?? ""}`}>
|
|
452
|
-
<div class="sidebar-header">{props.desktop.header}</div>
|
|
453
|
-
<Show when={props.desktop.actions}>
|
|
454
|
-
<div class="flex flex-col gap-3">{props.desktop.actions}</div>
|
|
455
|
-
</Show>
|
|
456
|
-
<Show when={props.desktop.body}>
|
|
457
|
-
<div class={`sidebar-body ${props.desktop.actions ? "mt-2" : ""}`}>{props.desktop.body}</div>
|
|
458
|
-
</Show>
|
|
459
|
-
<Show when={props.desktop.footer}>
|
|
460
|
-
<div class="sidebar-footer">{props.desktop.footer}</div>
|
|
461
|
-
</Show>
|
|
462
|
-
</aside>
|
|
463
|
-
</Show>
|
|
464
|
-
</>
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
export default SidebarLayout;
|
|
File without changes
|