@flikk/ui 1.0.0-beta.26 → 1.0.0-beta.27
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/dist/components/ai/PromptInput/PromptInput.js +3 -0
- package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -0
- package/dist/components/core/Button/Button.theme.js +1 -1
- package/dist/components/core/Calendar/Calendar.theme.js +1 -1
- package/dist/components/core/CommandPalette/CommandItem.js +4 -5
- package/dist/components/core/CommandPalette/CommandPalette.js +1 -1
- package/dist/components/core/CommandPalette/CommandPalette.theme.js +11 -13
- package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +0 -2
- package/dist/components/core/ContextMenu/ContextMenu.theme.js +13 -14
- package/dist/components/core/DotSeparator/DotSeparator.d.ts +3 -0
- package/dist/components/core/DotSeparator/DotSeparator.js +19 -0
- package/dist/components/core/DotSeparator/DotSeparator.theme.d.ts +2 -0
- package/dist/components/core/DotSeparator/DotSeparator.theme.js +10 -0
- package/dist/components/core/DotSeparator/DotSeparator.types.d.ts +25 -0
- package/dist/components/core/DotSeparator/index.d.ts +3 -0
- package/dist/components/core/Dropdown/Dropdown.theme.js +2 -3
- package/dist/components/core/Dropdown/DropdownItem.js +1 -1
- package/dist/components/core/Dropdown/DropdownMenu.js +1 -1
- package/dist/components/core/MenuItem/MenuItem.js +5 -2
- package/dist/components/core/MenuItem/MenuItem.theme.d.ts +6 -0
- package/dist/components/core/MenuItem/MenuItem.theme.js +19 -16
- package/dist/components/core/MenuItem/MenuItem.types.d.ts +2 -0
- package/dist/components/core/NavItem/NavItem.js +20 -4
- package/dist/components/core/NavItem/NavItem.theme.js +13 -2
- package/dist/components/core/Popover/Popover.types.d.ts +6 -2
- package/dist/components/core/Popover/PopoverBody.js +10 -3
- package/dist/components/core/Segmented/Segmented.theme.js +1 -1
- package/dist/components/core/Sidebar/Sidebar.d.ts +2 -0
- package/dist/components/core/Sidebar/Sidebar.js +15 -7
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +92 -4
- package/dist/components/core/Sidebar/SidebarCard.d.ts +25 -0
- package/dist/components/core/Sidebar/SidebarCard.js +68 -0
- package/dist/components/core/Sidebar/SidebarCard.theme.d.ts +8 -0
- package/dist/components/core/Sidebar/SidebarCard.theme.js +18 -0
- package/dist/components/core/Sidebar/SidebarContent.js +5 -1
- package/dist/components/core/Sidebar/SidebarContext.js +1 -1
- package/dist/components/core/Sidebar/SidebarHeader.js +10 -2
- package/dist/components/core/Sidebar/SidebarLogo.js +38 -3
- package/dist/components/core/Sidebar/SidebarSearch.d.ts +22 -0
- package/dist/components/core/Sidebar/SidebarSearch.js +135 -0
- package/dist/components/core/Sidebar/SidebarToggle.js +1 -1
- package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
- package/dist/components/core/Sidebar/index.d.ts +3 -1
- package/dist/components/core/Tag/Tag.js +29 -5
- package/dist/components/core/Tag/Tag.theme.js +13 -8
- package/dist/components/core/Tag/Tag.types.d.ts +7 -0
- package/dist/components/core/Tooltip/Tooltip.js +1 -1
- package/dist/components/core/Tooltip/Tooltip.theme.js +1 -2
- package/dist/components/core/index.d.ts +1 -0
- package/dist/components/core/index.js +4 -0
- package/dist/components/data-display/GanttChart/GanttChart.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttChart.js +31 -4
- package/dist/components/data-display/GanttChart/GanttChart.theme.js +19 -17
- package/dist/components/data-display/GanttChart/GanttChart.types.d.ts +19 -0
- package/dist/components/data-display/GanttChart/GanttChart.utils.js +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.d.ts +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.js +3 -3
- package/dist/components/data-display/GanttChart/GanttDependencyArrows.js +7 -4
- package/dist/components/data-display/GanttChart/GanttMilestone.js +5 -5
- package/dist/components/data-display/GanttChart/GanttTablePanel.js +1 -1
- package/dist/components/data-display/GanttChart/GanttTableTaskRow.js +2 -2
- package/dist/components/data-display/GanttChart/GanttTaskBar.js +37 -16
- package/dist/components/data-display/GanttChart/GanttTimelineGrid.js +2 -1
- package/dist/components/data-display/GanttChart/GanttTimelineHeader.js +35 -11
- package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
- package/dist/components/data-display/GanttChart/hooks/useGanttDrag.js +3 -2
- package/dist/components/data-display/GanttChart/index.d.ts +1 -1
- package/dist/components/data-display/Table/Table.theme.js +3 -5
- package/dist/components/effects/Aurora/Aurora.d.ts +6 -17
- package/dist/components/effects/Aurora/Aurora.js +297 -131
- package/dist/components/effects/Aurora/Aurora.types.d.ts +6 -0
- package/dist/components/forms/Combobox/Combobox.js +1 -1
- package/dist/components/forms/Combobox/Combobox.theme.js +2 -4
- package/dist/components/forms/CronInput/CronInput.js +1 -1
- package/dist/components/forms/CronInput/CronInput.theme.js +14 -23
- package/dist/components/forms/DatePicker/DatePicker.theme.js +11 -18
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +6 -13
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +1 -1
- package/dist/components/forms/FileUpload/FileUpload.animations.d.ts +29 -0
- package/dist/components/forms/FileUpload/FileUpload.animations.js +19 -0
- package/dist/components/forms/FileUpload/FileUpload.js +87 -48
- package/dist/components/forms/FileUpload/FileUpload.theme.d.ts +2 -10
- package/dist/components/forms/FileUpload/FileUpload.theme.js +16 -30
- package/dist/components/forms/FileUpload/FileUpload.types.d.ts +26 -10
- package/dist/components/forms/Input/Input.theme.js +13 -10
- package/dist/components/forms/InputCounter/InputCounter.theme.js +1 -1
- package/dist/components/forms/Mention/Mention.js +152 -57
- package/dist/components/forms/Mention/Mention.theme.js +16 -23
- package/dist/components/forms/Mention/Mention.types.d.ts +12 -12
- package/dist/components/forms/Mention/Mention.utils.js +32 -4
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +3 -3
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +5 -5
- package/dist/components/forms/Select/Select.js +2 -2
- package/dist/components/forms/Select/Select.theme.js +3 -3
- package/dist/components/forms/TimePicker/TimePickerContent.js +3 -0
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +1 -1
- package/dist/components/forms/forms.theme.js +21 -16
- package/dist/components/forms/index.js +3 -0
- package/dist/components/layout/AppShell/AppShell.d.ts +10 -0
- package/dist/components/layout/AppShell/AppShell.js +84 -0
- package/dist/components/layout/AppShell/AppShell.theme.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShell.theme.js +42 -0
- package/dist/components/layout/AppShell/AppShell.types.d.ts +127 -0
- package/dist/components/layout/AppShell/AppShellContext.d.ts +14 -0
- package/dist/components/layout/AppShell/AppShellContext.js +19 -0
- package/dist/components/layout/AppShell/AppShellFooter.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellFooter.js +12 -0
- package/dist/components/layout/AppShell/AppShellHeader.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellHeader.js +12 -0
- package/dist/components/layout/AppShell/AppShellMain.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellMain.js +19 -0
- package/dist/components/layout/AppShell/AppShellRightPanel.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellRightPanel.js +16 -0
- package/dist/components/layout/AppShell/AppShellSidebar.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellSidebar.js +16 -0
- package/dist/components/layout/AppShell/AppShellTopbar.d.ts +3 -0
- package/dist/components/layout/AppShell/AppShellTopbar.js +12 -0
- package/dist/components/layout/AppShell/index.d.ts +3 -0
- package/dist/components/layout/FormLayout/FormLayout.js +3 -0
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.js +2 -0
- package/dist/hooks/useSelectPortal.d.ts +10 -2
- package/dist/hooks/useSelectPortal.js +45 -24
- package/dist/index.js +6 -0
- package/dist/registry.json +10021 -0
- package/dist/styles.css +1 -1
- package/dist/utils/dateUtils.js +11 -1
- package/package.json +4 -2
- package/src/global.scss +29 -8
- package/src/styles/theme.css +51 -12
|
@@ -71,26 +71,21 @@ export interface MentionTheme {
|
|
|
71
71
|
inputGroupStyle: string;
|
|
72
72
|
/** JS-driven focus ring style */
|
|
73
73
|
inputGroupFocusStyle: string;
|
|
74
|
+
/** Invalid state style (inset box-shadow + danger fill) */
|
|
75
|
+
invalidStyle: string;
|
|
76
|
+
/** Invalid + focused style (danger inset shadow + ring glow) */
|
|
77
|
+
invalidFocusStyle: string;
|
|
74
78
|
/** Base editable div style */
|
|
75
79
|
editableStyle: string;
|
|
76
80
|
/** Size variants for the editable div */
|
|
77
81
|
sizes: Record<MentionSize, string>;
|
|
78
|
-
/** Inline mention Tag chip style */
|
|
79
|
-
mentionTagStyle: string;
|
|
80
82
|
/** Dropdown container style */
|
|
81
83
|
dropdownStyle: string;
|
|
82
|
-
/**
|
|
84
|
+
/** Suggestion row style — used only by the custom `renderSuggestion` path
|
|
85
|
+
* (default rows render via the shared MenuItem primitive). */
|
|
83
86
|
suggestionStyle: string;
|
|
84
|
-
/** Highlighted (keyboard-focused) suggestion style */
|
|
87
|
+
/** Highlighted (keyboard-focused) suggestion style — custom renderSuggestion path */
|
|
85
88
|
suggestionHighlightedStyle: string;
|
|
86
|
-
/** Avatar style inside a suggestion */
|
|
87
|
-
suggestionAvatarStyle: string;
|
|
88
|
-
/** Avatar fallback (initials) style */
|
|
89
|
-
suggestionAvatarFallbackStyle: string;
|
|
90
|
-
/** Suggestion label text style */
|
|
91
|
-
suggestionLabelStyle: string;
|
|
92
|
-
/** Suggestion description text style */
|
|
93
|
-
suggestionDescriptionStyle: string;
|
|
94
89
|
/** Empty state message style */
|
|
95
90
|
emptyMessageStyle: string;
|
|
96
91
|
/** Loading state style */
|
|
@@ -153,6 +148,11 @@ export interface MentionProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
|
|
|
153
148
|
mentionTagSize?: TagSize;
|
|
154
149
|
/** Additional className for inline mention Tag chips */
|
|
155
150
|
mentionTagClassName?: string;
|
|
151
|
+
/**
|
|
152
|
+
* Show a remove (×) button on each inline mention chip so it can be deleted
|
|
153
|
+
* by click as well as by keyboard. Defaults to `true`.
|
|
154
|
+
*/
|
|
155
|
+
mentionTagRemovable?: boolean;
|
|
156
156
|
/** Number of visible text rows (controls min-height) */
|
|
157
157
|
rows?: number;
|
|
158
158
|
/** Label rendered above the mention input */
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
// Data attribute used to identify mention nodes in the DOM
|
|
2
2
|
const MENTION_ATTR = "data-mention-id";
|
|
3
|
+
/**
|
|
4
|
+
* True when `node` lives inside a mention chip (its avatar/label text nodes),
|
|
5
|
+
* as opposed to being the mention element itself. A TreeWalker descends into a
|
|
6
|
+
* FILTER_ACCEPTed mention element, so without rejecting these inner nodes the
|
|
7
|
+
* offset walkers would count a chip's inner text and land the caret inside it
|
|
8
|
+
* (chips are contentEditable=false). Stops at `container` (the walker root).
|
|
9
|
+
*/
|
|
10
|
+
const isInsideMention = (node, container) => {
|
|
11
|
+
let parent = node.parentNode;
|
|
12
|
+
while (parent && parent !== container) {
|
|
13
|
+
if (parent.nodeType === Node.ELEMENT_NODE &&
|
|
14
|
+
parent.hasAttribute(MENTION_ATTR)) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
parent = parent.parentNode;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
3
21
|
/**
|
|
4
22
|
* Serialize segments to a plain text string.
|
|
5
23
|
* Mentions become `trigger + label` (e.g. "@Sarah Wilson").
|
|
@@ -111,12 +129,17 @@ const getPlainTextOffset = (container) => {
|
|
|
111
129
|
let offset = 0;
|
|
112
130
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {
|
|
113
131
|
acceptNode: (node) => {
|
|
114
|
-
|
|
115
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
132
|
+
// Mention element: count it atomically (its inner nodes are rejected below)
|
|
116
133
|
if (node.nodeType === Node.ELEMENT_NODE &&
|
|
117
134
|
node.hasAttribute(MENTION_ATTR)) {
|
|
118
135
|
return NodeFilter.FILTER_ACCEPT;
|
|
119
136
|
}
|
|
137
|
+
// Reject the chip's inner avatar/label nodes so the walker never
|
|
138
|
+
// descends into a mention or counts its inner text twice.
|
|
139
|
+
if (isInsideMention(node, container))
|
|
140
|
+
return NodeFilter.FILTER_REJECT;
|
|
141
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
142
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
120
143
|
// Skip non-mention element nodes but traverse their children
|
|
121
144
|
return NodeFilter.FILTER_SKIP;
|
|
122
145
|
},
|
|
@@ -234,12 +257,17 @@ const setCaretAtOffset = (container, targetOffset) => {
|
|
|
234
257
|
let remaining = targetOffset;
|
|
235
258
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {
|
|
236
259
|
acceptNode: (node) => {
|
|
237
|
-
|
|
238
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
260
|
+
// Mention element: count it atomically (its inner nodes are rejected below)
|
|
239
261
|
if (node.nodeType === Node.ELEMENT_NODE &&
|
|
240
262
|
node.hasAttribute(MENTION_ATTR)) {
|
|
241
263
|
return NodeFilter.FILTER_ACCEPT;
|
|
242
264
|
}
|
|
265
|
+
// Reject the chip's inner avatar/label nodes so the caret is never
|
|
266
|
+
// placed inside a mention (chips are contentEditable=false).
|
|
267
|
+
if (isInsideMention(node, container))
|
|
268
|
+
return NodeFilter.FILTER_REJECT;
|
|
269
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
270
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
243
271
|
return NodeFilter.FILTER_SKIP;
|
|
244
272
|
},
|
|
245
273
|
});
|
|
@@ -1437,11 +1437,11 @@ const RichTextEditor = React__default.forwardRef(({ size = "md", state = "defaul
|
|
|
1437
1437
|
: false;
|
|
1438
1438
|
switch (tool) {
|
|
1439
1439
|
case "bold":
|
|
1440
|
-
return (jsx(Button, { size: "sm", color: "neutral", variant: "soft", iconOnly: true, className: cn(themeStyles.toolbarButtonStyle), "data-state": isActive ? "active" : "inactive", onMouseDown: handleMouseDown, onClick: handleBold, disabled: isDisabled, "aria-label": "Bold", "aria-pressed": isActive ? "true" : "false", children: jsx(BoldIcon, { className: "
|
|
1440
|
+
return (jsx(Button, { size: "sm", color: "neutral", variant: "soft", iconOnly: true, className: cn(themeStyles.toolbarButtonStyle), "data-state": isActive ? "active" : "inactive", onMouseDown: handleMouseDown, onClick: handleBold, disabled: isDisabled, "aria-label": "Bold", "aria-pressed": isActive ? "true" : "false", children: jsx(BoldIcon, { className: "size-4" }) }, toolKey));
|
|
1441
1441
|
case "italic":
|
|
1442
|
-
return (jsx(Button, { size: "sm", color: "neutral", variant: "soft", iconOnly: true, className: cn(themeStyles.toolbarButtonStyle), "data-state": isActive ? "active" : "inactive", onMouseDown: handleMouseDown, onClick: handleItalic, disabled: isDisabled, "aria-label": "Italic", "aria-pressed": isActive ? "true" : "false", children: jsx(ItalicIcon, { className: "
|
|
1442
|
+
return (jsx(Button, { size: "sm", color: "neutral", variant: "soft", iconOnly: true, className: cn(themeStyles.toolbarButtonStyle), "data-state": isActive ? "active" : "inactive", onMouseDown: handleMouseDown, onClick: handleItalic, disabled: isDisabled, "aria-label": "Italic", "aria-pressed": isActive ? "true" : "false", children: jsx(ItalicIcon, { className: "size-4" }) }, toolKey));
|
|
1443
1443
|
case "underline":
|
|
1444
|
-
return (jsx(Button, { size: "sm", color: "neutral", variant: "soft", iconOnly: true, className: cn(themeStyles.toolbarButtonStyle), "data-state": isActive ? "active" : "inactive", onMouseDown: handleMouseDown, onClick: handleUnderline, disabled: isDisabled, "aria-label": "Underline", "aria-pressed": isActive ? "true" : "false", children: jsx(UnderlineIcon, { className: "
|
|
1444
|
+
return (jsx(Button, { size: "sm", color: "neutral", variant: "soft", iconOnly: true, className: cn(themeStyles.toolbarButtonStyle), "data-state": isActive ? "active" : "inactive", onMouseDown: handleMouseDown, onClick: handleUnderline, disabled: isDisabled, "aria-label": "Underline", "aria-pressed": isActive ? "true" : "false", children: jsx(UnderlineIcon, { className: "size-4" }) }, toolKey));
|
|
1445
1445
|
case "fontFamily": {
|
|
1446
1446
|
// The `fontFamily` tool is opt-in via toolbar config, but
|
|
1447
1447
|
// `fontFamilies` is also a public prop — if a consumer passes an
|
|
@@ -97,16 +97,16 @@ const richTextEditorTheme = {
|
|
|
97
97
|
// `rounded-t-[var(--form-radius)]` matches the container's radius so the
|
|
98
98
|
// toolbar's own bg/backdrop-blur respects the top corners — prevents the
|
|
99
99
|
// 1px bleed past the parent's `overflow-hidden` clip in Safari/Chromium.
|
|
100
|
-
toolbarStyle: "flex items-center gap-
|
|
100
|
+
toolbarStyle: "flex items-center m-0.5 gap-0.5 p-0.5 rounded-[var(--form-radius)] dark:bg-[var(--color-surface)]/0",
|
|
101
101
|
// Toolbar styling (floating variant — selection-anchored)
|
|
102
|
-
floatingToolbarStyle: "flex items-center gap-1 p-1 rounded-xl border border-[var(--color-border)] bg-
|
|
102
|
+
floatingToolbarStyle: "flex items-center gap-1 p-1 rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-overlay)]/70 backdrop-blur-sm shadow-real-xl overflow-hidden",
|
|
103
103
|
// Toolbar button — single source of truth via data-[state=active]
|
|
104
104
|
// (aria-pressed remains as proper ARIA on toggle buttons, but styling is decoupled)
|
|
105
|
-
toolbarButtonStyle: "inline-flex items-center justify-center
|
|
105
|
+
toolbarButtonStyle: "inline-flex items-center justify-center rounded-[calc(var(--radius-base)*0.85)] border border-transparent bg-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-background-secondary)] transition-colors duration-300 disabled:opacity-50 disabled:cursor-not-allowed data-[state=active]:bg-[var(--color-background-secondary)] data-[state=active]:text-[var(--color-text-primary)] data-[state=active]:border-[var(--color-border)] data-[state=active]:shadow-inner",
|
|
106
106
|
// Dropdown surface (visual only — positioning handled at use site via portal)
|
|
107
|
-
dropdownStyle: "min-w-[120px] max-h-[18rem] overflow-y-auto py-1 bg-
|
|
107
|
+
dropdownStyle: "min-w-[120px] max-h-[18rem] overflow-y-auto py-1 bg-[var(--color-surface-overlay)]/70 backdrop-blur-md rounded-[var(--form-radius)] shadow-xl ring-1 ring-[var(--color-border)] focus:outline-none",
|
|
108
108
|
// Dropdown item — uses data-[active=true] for selected state
|
|
109
|
-
dropdownItemStyle: "block w-full px-3 py-2 text-left text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed data-[active=true]:bg-[var(--color-primary-50)] data-[active=true]:text-[var(--color-primary-900)]",
|
|
109
|
+
dropdownItemStyle: "block w-full px-3 py-2 text-left text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed data-[active=true]:bg-[var(--color-primary-50)] data-[active=true]:text-[var(--color-primary-900)] data-[active=true]:dark:bg-[var(--color-primary-400)]/10 data-[active=true]:dark:text-[var(--color-primary-400)]",
|
|
110
110
|
// Separator between toolbar sections
|
|
111
111
|
separatorStyle: "w-px h-6 bg-[var(--color-border)] mx-1",
|
|
112
112
|
// Color picker layout — column of family rows, plus optional Recent + Mono rows
|
|
@@ -96,7 +96,7 @@ const SelectButton = ({ id, disabled, isOpen, state = "default", size = "md", ic
|
|
|
96
96
|
// Use either the themed focus state or directly apply the focus classes if the theme is not working
|
|
97
97
|
const activeFocusState = isOpen
|
|
98
98
|
? focusStateStyle ||
|
|
99
|
-
"shadow-[
|
|
99
|
+
"shadow-[inset_0_0_0_1px_var(--color-primary)] ring-4 ring-[var(--color-primary)]/10"
|
|
100
100
|
: "";
|
|
101
101
|
return (jsxs("div", { className: cn(buttonWrapperStyle, "flex items-stretch"), children: [contentStart && (jsx("div", { "data-slot": "content-start", className: cn(contentSlotStyle, "pr-0 border-r border-[var(--color-border)]"), children: contentStart })), jsxs("button", { ref: buttonRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconStartPadding, iconEndPadding, buttonStyle, "flex-1 min-w-0", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": isOpen && id ? `${id}-listbox` : undefined, "aria-labelledby": ariaLabelledby, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, children: [iconStart && jsx("span", { className: iconStartStyle, children: iconStart }), children, iconEnd && jsx("span", { className: iconEndStyle, children: iconEnd })] }), contentEnd && (jsx("div", { "data-slot": "content-end", className: cn(contentSlotStyle, "pl-0 border-l border-[var(--color-border)]"), children: contentEnd }))] }));
|
|
102
102
|
};
|
|
@@ -287,7 +287,7 @@ const SelectOptions = ({ isOpen, maxHeight = "15rem", className = "", onClose, c
|
|
|
287
287
|
} }) })), children] })) }));
|
|
288
288
|
// Use portal if enabled and client-side - wrap with dark class for dark mode support
|
|
289
289
|
if (portal && isClient) {
|
|
290
|
-
return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: optionsContent }), document.body);
|
|
290
|
+
return createPortal(jsx("div", { "data-flikkui-portal": "", className: isDarkMode ? "dark" : undefined, children: optionsContent }), document.body);
|
|
291
291
|
}
|
|
292
292
|
return optionsContent;
|
|
293
293
|
};
|
|
@@ -11,12 +11,12 @@ const selectTheme = {
|
|
|
11
11
|
baseStyle: formsBaseTheme.inputGroupBaseStyle + " items-center justify-between",
|
|
12
12
|
// Button wrapper style
|
|
13
13
|
buttonWrapperStyle: "relative w-full",
|
|
14
|
-
// Button style -
|
|
15
|
-
buttonStyle: "w-full cursor-pointer bg-
|
|
14
|
+
// Button style - surface flips automatically between light/dark
|
|
15
|
+
buttonStyle: "w-full cursor-pointer bg-[var(--color-surface)]",
|
|
16
16
|
// Placeholder text style - matches Input placeholder styling
|
|
17
17
|
placeholderTextStyle: formsBaseTheme.placeholderStyle,
|
|
18
18
|
// Selected value text style
|
|
19
|
-
valueTextStyle: "text-[var(--color-text-primary)]
|
|
19
|
+
valueTextStyle: "text-[var(--color-text-primary)]",
|
|
20
20
|
// Size variants - composed from atomic tokens
|
|
21
21
|
sizes: {
|
|
22
22
|
sm: `${formsBaseTheme.sizes.sm.text} ${formsBaseTheme.sizes.sm.padding} ${formsBaseTheme.sizes.sm.height}`,
|
|
@@ -35,6 +35,7 @@ import '../../core/Dropdown/DropdownItem.js';
|
|
|
35
35
|
import '../../core/Dropdown/DropdownSection.js';
|
|
36
36
|
import '../../core/Dropdown/DropdownSeparator.js';
|
|
37
37
|
import '../../core/Dropdown/Dropdown.theme.js';
|
|
38
|
+
import '../../core/DotSeparator/DotSeparator.js';
|
|
38
39
|
import '../../core/Kbd/Kbd.js';
|
|
39
40
|
import '../../core/MenuItem/MenuItem.js';
|
|
40
41
|
import '../../core/Link/Link.js';
|
|
@@ -86,6 +87,8 @@ import '../../core/Sidebar/SidebarToggle.js';
|
|
|
86
87
|
import '../../core/Sidebar/SidebarMobileTrigger.js';
|
|
87
88
|
import '../../core/Sidebar/SidebarUserProfile.js';
|
|
88
89
|
import '../../core/Sidebar/SidebarLogo.js';
|
|
90
|
+
import '../../core/Sidebar/SidebarSearch.js';
|
|
91
|
+
import '../../core/Sidebar/SidebarCard.js';
|
|
89
92
|
import '../../core/Sidebar/SidebarContext.js';
|
|
90
93
|
import '../../core/OfflineIndicator/OfflineIndicator.js';
|
|
91
94
|
import '../../core/ContextMenu/ContextMenu.js';
|
|
@@ -25,7 +25,7 @@ const TimePickerTrigger = ({ id, disabled, isOpen, state = 'default', size = 'md
|
|
|
25
25
|
const periodToggleStyle = theme.periodToggleStyle || "";
|
|
26
26
|
// Apply focused styles when open — same pattern as SelectButton and DatePickerTrigger
|
|
27
27
|
const activeFocusState = isOpen
|
|
28
|
-
? ((_d = theme.triggerFocusStates) === null || _d === void 0 ? void 0 : _d[state]) || "shadow-[
|
|
28
|
+
? ((_d = theme.triggerFocusStates) === null || _d === void 0 ? void 0 : _d[state]) || "shadow-[inset_0_0_0_1px_var(--color-primary-600)] ring-4 ring-[var(--color-primary)]/10 dark:shadow-[inset_0_0_0_1px_var(--color-primary-500)] dark:ring-[var(--color-primary-500)]/20"
|
|
29
29
|
: "";
|
|
30
30
|
// Determine whether start icon is present (for input padding)
|
|
31
31
|
const hasStartIcon = iconStart !== undefined ? Boolean(iconStart) : true;
|
|
@@ -9,11 +9,13 @@ const formsBaseTheme = {
|
|
|
9
9
|
wrapperStyle: "relative w-full",
|
|
10
10
|
// Common input group style with focus, hover, and state variations
|
|
11
11
|
inputGroupBaseStyle: "relative flex rounded-[var(--form-radius)] outline-none shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
12
|
-
"focus-within:shadow-[
|
|
13
|
-
"focus-within:ring-4 focus-within:ring-[var(--color-primary)]/10 bg-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
"
|
|
12
|
+
"focus-within:shadow-[inset_0_0_0_1px_var(--color-primary-600)] " +
|
|
13
|
+
"focus-within:ring-4 focus-within:ring-[var(--color-primary)]/10 bg-[var(--color-surface)] transition-all " +
|
|
14
|
+
// Hover edge: strengthen the border to --color-border-secondary on hover. Scoped with
|
|
15
|
+
// not-focus-within so focus (primary border) always wins; the token flips for dark mode.
|
|
16
|
+
"not-focus-within:hover:shadow-[inset_0_0_0_1px_var(--color-border-secondary)] " +
|
|
17
|
+
// Dark focus mirror (fill comes from --color-surface, which flips automatically)
|
|
18
|
+
"dark:focus-within:shadow-[inset_0_0_0_1px_var(--color-primary-500)] dark:focus-within:ring-[var(--color-primary-500)]/20",
|
|
17
19
|
// Common base input/control style
|
|
18
20
|
controlBaseStyle: "w-full bg-transparent text-sm text-[var(--color-text-primary)] " +
|
|
19
21
|
"placeholder:text-[var(--color-text-placeholder)] focus:outline-none " +
|
|
@@ -45,24 +47,28 @@ const formsBaseTheme = {
|
|
|
45
47
|
states: {
|
|
46
48
|
default: "",
|
|
47
49
|
disabled: "cursor-not-allowed bg-[var(--color-background-disabled)] shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
50
|
+
// Suppress the hover-edge change while disabled (keep the resting border)
|
|
51
|
+
"not-focus-within:hover:shadow-[inset_0_0_0_1px_var(--color-border)] " +
|
|
48
52
|
"[&_input]:text-[var(--color-text-disabled)] [&_textarea]:text-[var(--color-text-disabled)] " +
|
|
49
53
|
"[&_select]:text-[var(--color-text-disabled)] [&_input]:cursor-not-allowed " +
|
|
50
54
|
"[&_textarea]:cursor-not-allowed [&_select]:cursor-not-allowed " +
|
|
51
55
|
"[&_input]:placeholder:text-[var(--color-text-placeholder)] " +
|
|
52
56
|
"[&_textarea]:placeholder:text-[var(--color-text-placeholder)] " +
|
|
53
|
-
"[&_select]:placeholder:text-[var(--color-text-placeholder)] [&_button]:cursor-not-allowed
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
"[&_select]:placeholder:text-[var(--color-text-placeholder)] [&_button]:cursor-not-allowed",
|
|
58
|
+
// (--color-background-disabled already flips for dark mode)
|
|
59
|
+
invalid: "shadow-[inset_0_0_0_1px_var(--color-danger)] bg-[var(--color-danger-50)] focus-within:shadow-[inset_0_0_0_1px_var(--color-danger)] " +
|
|
60
|
+
// Keep the danger border on hover (don't let the hover edge override it)
|
|
61
|
+
"not-focus-within:hover:shadow-[inset_0_0_0_1px_var(--color-danger)] " +
|
|
57
62
|
// Dark mode
|
|
58
|
-
"dark:bg-[var(--color-danger-950)]/30 dark:shadow-[inset_0_0_0_1px_var(--color-danger-500)]"
|
|
63
|
+
"dark:bg-[var(--color-danger-950)]/30 dark:shadow-[inset_0_0_0_1px_var(--color-danger-500)] " +
|
|
64
|
+
"dark:not-focus-within:hover:shadow-[inset_0_0_0_1px_var(--color-danger-500)]",
|
|
59
65
|
},
|
|
60
66
|
// Common focus state variants
|
|
61
67
|
focusStates: {
|
|
62
|
-
default: "shadow-[
|
|
63
|
-
"dark:shadow-[
|
|
68
|
+
default: "shadow-[inset_0_0_0_1px_var(--color-primary-600)] ring-4 ring-[var(--color-primary)]/10 " +
|
|
69
|
+
"dark:shadow-[inset_0_0_0_1px_var(--color-primary-500)] dark:ring-[var(--color-primary-500)]/20",
|
|
64
70
|
disabled: "shadow-none",
|
|
65
|
-
invalid: "shadow-[
|
|
71
|
+
invalid: "shadow-[inset_0_0_0_1px_var(--color-danger-500)] ring-4 ring-[var(--color-danger-500)]/10 " +
|
|
66
72
|
"dark:ring-[var(--color-danger-500)]/20",
|
|
67
73
|
},
|
|
68
74
|
// Common hover state variants
|
|
@@ -89,9 +95,8 @@ const formsBaseTheme = {
|
|
|
89
95
|
dropdownStyles: {
|
|
90
96
|
// Container for dropdown panel
|
|
91
97
|
container: "fixed z-[1000] max-h-[var(--select-content-available-height,20rem)] overflow-auto overscroll-contain " +
|
|
92
|
-
"rounded-[var(--form-radius)] bg-
|
|
93
|
-
"shadow-real-lg ring-1 ring-[var(--color-border)] focus:outline-none
|
|
94
|
-
"dark:bg-[var(--color-neutral-900)]/90 dark:ring-[var(--color-neutral-700)]",
|
|
98
|
+
"rounded-[var(--form-radius)] bg-[var(--color-surface-overlay)]/90 backdrop-blur-md text-sm border border-[var(--color-border)] " +
|
|
99
|
+
"shadow-real-lg ring-1 ring-[var(--color-border)] focus:outline-none",
|
|
95
100
|
// Base option style
|
|
96
101
|
option: "relative cursor-pointer select-none py-2 px-3 text-[var(--color-text-primary)] " +
|
|
97
102
|
"hover:text-[var(--color-primary)] hover:bg-[var(--color-primary-50)]/50 " +
|
|
@@ -58,6 +58,7 @@ import '../core/Dropdown/DropdownItem.js';
|
|
|
58
58
|
import '../core/Dropdown/DropdownSection.js';
|
|
59
59
|
import '../core/Dropdown/DropdownSeparator.js';
|
|
60
60
|
import '../core/Dropdown/Dropdown.theme.js';
|
|
61
|
+
import '../core/DotSeparator/DotSeparator.js';
|
|
61
62
|
import '../core/Kbd/Kbd.js';
|
|
62
63
|
import '../core/MenuItem/MenuItem.js';
|
|
63
64
|
import '../core/Link/Link.js';
|
|
@@ -108,6 +109,8 @@ import '../core/Sidebar/SidebarToggle.js';
|
|
|
108
109
|
import '../core/Sidebar/SidebarMobileTrigger.js';
|
|
109
110
|
import '../core/Sidebar/SidebarUserProfile.js';
|
|
110
111
|
import '../core/Sidebar/SidebarLogo.js';
|
|
112
|
+
import '../core/Sidebar/SidebarSearch.js';
|
|
113
|
+
import '../core/Sidebar/SidebarCard.js';
|
|
111
114
|
import '../core/Sidebar/SidebarContext.js';
|
|
112
115
|
import '../core/OfflineIndicator/OfflineIndicator.js';
|
|
113
116
|
import '../core/ContextMenu/ContextMenu.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AppShellProps } from "./AppShell.types";
|
|
3
|
+
export declare const AppShell: React.ForwardRefExoticComponent<AppShellProps & React.RefAttributes<HTMLDivElement>> & {
|
|
4
|
+
Topbar: React.ForwardRefExoticComponent<import("./AppShell.types").AppShellTopbarProps & React.RefAttributes<HTMLElement>>;
|
|
5
|
+
Sidebar: React.ForwardRefExoticComponent<import("./AppShell.types").AppShellSidebarProps & React.RefAttributes<HTMLElement>>;
|
|
6
|
+
Header: React.ForwardRefExoticComponent<import("./AppShell.types").AppShellHeaderProps & React.RefAttributes<HTMLElement>>;
|
|
7
|
+
Main: React.ForwardRefExoticComponent<import("./AppShell.types").AppShellMainProps & React.RefAttributes<HTMLElement>>;
|
|
8
|
+
RightPanel: React.ForwardRefExoticComponent<import("./AppShell.types").AppShellRightPanelProps & React.RefAttributes<HTMLElement>>;
|
|
9
|
+
Footer: React.ForwardRefExoticComponent<import("./AppShell.types").AppShellFooterProps & React.RefAttributes<HTMLElement>>;
|
|
10
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import { cn } from '../../../utils/cn.js';
|
|
4
|
+
import { getAppShellTheme } from './AppShell.theme.js';
|
|
5
|
+
import { AppShellProvider } from './AppShellContext.js';
|
|
6
|
+
import { AppShellTopbar } from './AppShellTopbar.js';
|
|
7
|
+
import { AppShellSidebar } from './AppShellSidebar.js';
|
|
8
|
+
import { AppShellHeader } from './AppShellHeader.js';
|
|
9
|
+
import { AppShellMain } from './AppShellMain.js';
|
|
10
|
+
import { AppShellRightPanel } from './AppShellRightPanel.js';
|
|
11
|
+
import { AppShellFooter } from './AppShellFooter.js';
|
|
12
|
+
|
|
13
|
+
const AppShellRoot = React__default.forwardRef(({ className, theme: customTheme, scroll = "viewport", topbarSpan = "full", footerSpan = "full", contentWidth = "full", contentPadding = "md", children, ...props }, ref) => {
|
|
14
|
+
const theme = getAppShellTheme(customTheme);
|
|
15
|
+
const contextValue = React__default.useMemo(() => ({ scroll, contentWidth, contentPadding }), [scroll, contentWidth, contentPadding]);
|
|
16
|
+
// Bucket children by slot type. Header is intentionally NOT bucketed —
|
|
17
|
+
// it is user-composed inside AppShell.Main.
|
|
18
|
+
let topbar = null;
|
|
19
|
+
let sidebar = null;
|
|
20
|
+
let main = null;
|
|
21
|
+
let rightPanel = null;
|
|
22
|
+
let footer = null;
|
|
23
|
+
React__default.Children.forEach(children, (child) => {
|
|
24
|
+
if (!React__default.isValidElement(child))
|
|
25
|
+
return;
|
|
26
|
+
const type = child.type;
|
|
27
|
+
if (type === AppShellTopbar) {
|
|
28
|
+
if (topbar === null)
|
|
29
|
+
topbar = child;
|
|
30
|
+
else if (process.env.NODE_ENV !== "production") {
|
|
31
|
+
console.warn("[AppShell]: multiple AppShell.Topbar children; keeping the first.");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (type === AppShellSidebar) {
|
|
35
|
+
if (sidebar === null)
|
|
36
|
+
sidebar = child;
|
|
37
|
+
else if (process.env.NODE_ENV !== "production") {
|
|
38
|
+
console.warn("[AppShell]: multiple AppShell.Sidebar children; keeping the first.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (type === AppShellMain) {
|
|
42
|
+
if (main === null)
|
|
43
|
+
main = child;
|
|
44
|
+
else if (process.env.NODE_ENV !== "production") {
|
|
45
|
+
console.warn("[AppShell]: multiple AppShell.Main children; keeping the first.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (type === AppShellRightPanel) {
|
|
49
|
+
if (rightPanel === null)
|
|
50
|
+
rightPanel = child;
|
|
51
|
+
else if (process.env.NODE_ENV !== "production") {
|
|
52
|
+
console.warn("[AppShell]: multiple AppShell.RightPanel children; keeping the first.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (type === AppShellFooter) {
|
|
56
|
+
if (footer === null)
|
|
57
|
+
footer = child;
|
|
58
|
+
else if (process.env.NODE_ENV !== "production") {
|
|
59
|
+
console.warn("[AppShell]: multiple AppShell.Footer children; keeping the first.");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (process.env.NODE_ENV !== "production") {
|
|
63
|
+
console.warn("[AppShell]: unknown child rendered; expected one of " +
|
|
64
|
+
"AppShell.Topbar / Sidebar / Main / RightPanel / Footer. " +
|
|
65
|
+
"AppShell.Header is composed inside AppShell.Main.");
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const topbarInChrome = topbar !== null && topbarSpan === "full";
|
|
69
|
+
const topbarInMain = topbar !== null && topbarSpan === "main";
|
|
70
|
+
const footerInChrome = footer !== null && footerSpan === "full";
|
|
71
|
+
const footerInMain = footer !== null && footerSpan === "main";
|
|
72
|
+
return (jsx(AppShellProvider, { value: contextValue, children: jsxs("div", { ref: ref, className: cn(theme.root, scroll === "viewport" && theme.rootViewport, className), ...props, children: [topbarInChrome && topbar, jsxs("div", { className: theme.shell, children: [sidebar, jsxs("div", { className: theme.mainColumn, children: [topbarInMain && topbar, main, footerInMain && footer] }), rightPanel] }), footerInChrome && footer] }) }));
|
|
73
|
+
});
|
|
74
|
+
AppShellRoot.displayName = "AppShell";
|
|
75
|
+
const AppShell = Object.assign(AppShellRoot, {
|
|
76
|
+
Topbar: AppShellTopbar,
|
|
77
|
+
Sidebar: AppShellSidebar,
|
|
78
|
+
Header: AppShellHeader,
|
|
79
|
+
Main: AppShellMain,
|
|
80
|
+
RightPanel: AppShellRightPanel,
|
|
81
|
+
Footer: AppShellFooter,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export { AppShell };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const appShellTheme = {
|
|
2
|
+
root: "flex flex-col bg-[var(--color-background)] text-[var(--color-text-primary)]",
|
|
3
|
+
rootViewport: "h-screen overflow-hidden",
|
|
4
|
+
shell: "flex flex-1 min-h-0 min-w-0",
|
|
5
|
+
mainColumn: "flex flex-col flex-1 min-h-0 min-w-0",
|
|
6
|
+
topbar: "shrink-0 border-b border-[var(--color-border)] bg-[var(--color-background)]/90 backdrop-blur supports-[backdrop-filter]:bg-[var(--color-background)]/75",
|
|
7
|
+
sidebar: "flex flex-col shrink-0 overflow-hidden border-r border-[var(--color-border)] bg-[var(--color-background)]/95 backdrop-blur supports-[backdrop-filter]:bg-[var(--color-background)]/85",
|
|
8
|
+
sidebarScroll: "min-h-0 flex-1",
|
|
9
|
+
sidebarCollapsed: "hidden lg:flex",
|
|
10
|
+
header: "shrink-0 border-b border-[var(--color-border)] bg-[var(--color-background)]/90 backdrop-blur supports-[backdrop-filter]:bg-[var(--color-background)]/75",
|
|
11
|
+
main: "flex flex-col flex-1 min-h-0 min-w-0",
|
|
12
|
+
mainScroll: "min-h-0 flex-1",
|
|
13
|
+
mainInner: "mx-auto w-full",
|
|
14
|
+
rightPanel: "flex flex-col shrink-0 overflow-hidden border-l border-[var(--color-border)] bg-[var(--color-background)]/95 backdrop-blur supports-[backdrop-filter]:bg-[var(--color-background)]/85",
|
|
15
|
+
rightPanelScroll: "min-h-0 flex-1",
|
|
16
|
+
footer: "shrink-0 border-t border-[var(--color-border)] bg-[var(--color-background)]/90 backdrop-blur supports-[backdrop-filter]:bg-[var(--color-background)]/75",
|
|
17
|
+
contentPadding: {
|
|
18
|
+
none: "p-0",
|
|
19
|
+
sm: "p-3 sm:p-4",
|
|
20
|
+
md: "p-4 sm:p-6 lg:p-8",
|
|
21
|
+
lg: "p-5 sm:p-8 lg:p-10",
|
|
22
|
+
},
|
|
23
|
+
contentWidth: {
|
|
24
|
+
full: "max-w-none",
|
|
25
|
+
wide: "max-w-7xl",
|
|
26
|
+
contained: "max-w-5xl",
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
const getAppShellTheme = (customTheme) => ({
|
|
30
|
+
...appShellTheme,
|
|
31
|
+
...customTheme,
|
|
32
|
+
contentPadding: {
|
|
33
|
+
...appShellTheme.contentPadding,
|
|
34
|
+
...customTheme === null || customTheme === void 0 ? void 0 : customTheme.contentPadding,
|
|
35
|
+
},
|
|
36
|
+
contentWidth: {
|
|
37
|
+
...appShellTheme.contentWidth,
|
|
38
|
+
...customTheme === null || customTheme === void 0 ? void 0 : customTheme.contentWidth,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export { appShellTheme, getAppShellTheme };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type AppShellScroll = "viewport" | "page";
|
|
3
|
+
export type AppShellSpan = "full" | "main";
|
|
4
|
+
export type AppShellContentWidth = "full" | "wide" | "contained";
|
|
5
|
+
export type AppShellContentPadding = "none" | "sm" | "md" | "lg";
|
|
6
|
+
export interface AppShellTheme {
|
|
7
|
+
/** Outer root container (column layout). */
|
|
8
|
+
root: string;
|
|
9
|
+
/** Extra classes applied when `scroll="viewport"`. */
|
|
10
|
+
rootViewport: string;
|
|
11
|
+
/** Row container holding sidebar / main column / right panel. */
|
|
12
|
+
shell: string;
|
|
13
|
+
/** The middle column (Topbar-in-main? Main, Footer-in-main?). */
|
|
14
|
+
mainColumn: string;
|
|
15
|
+
topbar: string;
|
|
16
|
+
sidebar: string;
|
|
17
|
+
sidebarScroll: string;
|
|
18
|
+
sidebarCollapsed: string;
|
|
19
|
+
header: string;
|
|
20
|
+
main: string;
|
|
21
|
+
mainScroll: string;
|
|
22
|
+
mainInner: string;
|
|
23
|
+
rightPanel: string;
|
|
24
|
+
rightPanelScroll: string;
|
|
25
|
+
footer: string;
|
|
26
|
+
contentPadding: Record<AppShellContentPadding, string>;
|
|
27
|
+
contentWidth: Record<AppShellContentWidth, string>;
|
|
28
|
+
}
|
|
29
|
+
export interface AppShellThemeOverrides {
|
|
30
|
+
root?: string;
|
|
31
|
+
rootViewport?: string;
|
|
32
|
+
shell?: string;
|
|
33
|
+
mainColumn?: string;
|
|
34
|
+
topbar?: string;
|
|
35
|
+
sidebar?: string;
|
|
36
|
+
sidebarScroll?: string;
|
|
37
|
+
sidebarCollapsed?: string;
|
|
38
|
+
header?: string;
|
|
39
|
+
main?: string;
|
|
40
|
+
mainScroll?: string;
|
|
41
|
+
mainInner?: string;
|
|
42
|
+
rightPanel?: string;
|
|
43
|
+
rightPanelScroll?: string;
|
|
44
|
+
footer?: string;
|
|
45
|
+
contentPadding?: Partial<Record<AppShellContentPadding, string>>;
|
|
46
|
+
contentWidth?: Partial<Record<AppShellContentWidth, string>>;
|
|
47
|
+
}
|
|
48
|
+
export interface AppShellProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
49
|
+
/**
|
|
50
|
+
* Scroll model. `"viewport"` makes the root `h-screen overflow-hidden`
|
|
51
|
+
* and only `Main` scrolls. `"page"` lets the whole document scroll.
|
|
52
|
+
* @default "viewport"
|
|
53
|
+
*/
|
|
54
|
+
scroll?: AppShellScroll;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the Topbar spans the full viewport width (over the Sidebar)
|
|
57
|
+
* or only sits above Main + RightPanel.
|
|
58
|
+
* @default "full"
|
|
59
|
+
*/
|
|
60
|
+
topbarSpan?: AppShellSpan;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the Footer spans the full viewport width (under the Sidebar)
|
|
63
|
+
* or only sits below Main + RightPanel.
|
|
64
|
+
* @default "full"
|
|
65
|
+
*/
|
|
66
|
+
footerSpan?: AppShellSpan;
|
|
67
|
+
/**
|
|
68
|
+
* Max content width applied to AppShell.Main when not overridden.
|
|
69
|
+
* @default "full"
|
|
70
|
+
*/
|
|
71
|
+
contentWidth?: AppShellContentWidth;
|
|
72
|
+
/**
|
|
73
|
+
* Responsive content padding applied to AppShell.Main when not overridden.
|
|
74
|
+
* @default "md"
|
|
75
|
+
*/
|
|
76
|
+
contentPadding?: AppShellContentPadding;
|
|
77
|
+
/**
|
|
78
|
+
* Theme overrides for the AppShell root wrapper (root, shell, mainColumn).
|
|
79
|
+
* Style individual slots via their own `theme` props on each sub-component.
|
|
80
|
+
*/
|
|
81
|
+
theme?: AppShellThemeOverrides;
|
|
82
|
+
}
|
|
83
|
+
export interface AppShellTopbarProps extends React.HTMLAttributes<HTMLElement> {
|
|
84
|
+
/** Stick to top of chrome. Only meaningful when `scroll="page"`. @default true */
|
|
85
|
+
sticky?: boolean;
|
|
86
|
+
/** Theme overrides. */
|
|
87
|
+
theme?: AppShellThemeOverrides;
|
|
88
|
+
}
|
|
89
|
+
export interface AppShellSidebarProps extends React.HTMLAttributes<HTMLElement> {
|
|
90
|
+
/** CSS width value. @default "16rem" */
|
|
91
|
+
width?: string;
|
|
92
|
+
/** Hide on mobile (below `lg`). @default true */
|
|
93
|
+
collapsedOnMobile?: boolean;
|
|
94
|
+
/** Theme overrides. */
|
|
95
|
+
theme?: AppShellThemeOverrides;
|
|
96
|
+
}
|
|
97
|
+
export interface AppShellHeaderProps extends React.HTMLAttributes<HTMLElement> {
|
|
98
|
+
/**
|
|
99
|
+
* Stick to top of the scrolling Main region. Header sticks at its position
|
|
100
|
+
* inside Main's content padding; for a flush edge-to-edge sticky bar, use
|
|
101
|
+
* AppShell.Topbar instead.
|
|
102
|
+
* @default true
|
|
103
|
+
*/
|
|
104
|
+
sticky?: boolean;
|
|
105
|
+
/** Theme overrides. */
|
|
106
|
+
theme?: AppShellThemeOverrides;
|
|
107
|
+
}
|
|
108
|
+
export interface AppShellMainProps extends React.HTMLAttributes<HTMLElement> {
|
|
109
|
+
/** Max content width. Inherits from AppShell when omitted. */
|
|
110
|
+
contentWidth?: AppShellContentWidth;
|
|
111
|
+
/** Responsive padding. Inherits from AppShell when omitted. */
|
|
112
|
+
contentPadding?: AppShellContentPadding;
|
|
113
|
+
/** Theme overrides. */
|
|
114
|
+
theme?: AppShellThemeOverrides;
|
|
115
|
+
}
|
|
116
|
+
export interface AppShellRightPanelProps extends React.HTMLAttributes<HTMLElement> {
|
|
117
|
+
/** CSS width value. @default "20rem" */
|
|
118
|
+
width?: string;
|
|
119
|
+
/** Theme overrides. */
|
|
120
|
+
theme?: AppShellThemeOverrides;
|
|
121
|
+
}
|
|
122
|
+
export interface AppShellFooterProps extends React.HTMLAttributes<HTMLElement> {
|
|
123
|
+
/** Stick to bottom of chrome. Only meaningful when `scroll="page"`. @default true */
|
|
124
|
+
sticky?: boolean;
|
|
125
|
+
/** Theme overrides. */
|
|
126
|
+
theme?: AppShellThemeOverrides;
|
|
127
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AppShellContentPadding, AppShellContentWidth, AppShellScroll } from "./AppShell.types";
|
|
3
|
+
export interface AppShellContextValue {
|
|
4
|
+
scroll: AppShellScroll;
|
|
5
|
+
contentWidth: AppShellContentWidth;
|
|
6
|
+
contentPadding: AppShellContentPadding;
|
|
7
|
+
}
|
|
8
|
+
export declare const AppShellProvider: React.Provider<AppShellContextValue | undefined>;
|
|
9
|
+
/**
|
|
10
|
+
* Reads AppShell root context. Returns sensible defaults when used outside
|
|
11
|
+
* an `<AppShell>` (so individual slot components remain usable standalone
|
|
12
|
+
* for testing or partial composition).
|
|
13
|
+
*/
|
|
14
|
+
export declare const useAppShellContext: () => AppShellContextValue;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
|
|
3
|
+
const AppShellContext = React__default.createContext(undefined);
|
|
4
|
+
const AppShellProvider = AppShellContext.Provider;
|
|
5
|
+
/**
|
|
6
|
+
* Reads AppShell root context. Returns sensible defaults when used outside
|
|
7
|
+
* an `<AppShell>` (so individual slot components remain usable standalone
|
|
8
|
+
* for testing or partial composition).
|
|
9
|
+
*/
|
|
10
|
+
const useAppShellContext = () => {
|
|
11
|
+
const ctx = React__default.useContext(AppShellContext);
|
|
12
|
+
return (ctx !== null && ctx !== void 0 ? ctx : {
|
|
13
|
+
scroll: "viewport",
|
|
14
|
+
contentWidth: "full",
|
|
15
|
+
contentPadding: "md",
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { AppShellProvider, useAppShellContext };
|