@elabs-ai/components-ui 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/chunk-FCH4ZN6G.js +57 -0
- package/dist/chunk-FCH4ZN6G.js.map +1 -0
- package/dist/form.d.ts +28 -0
- package/dist/form.js +123 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +2597 -188
- package/dist/index.js +12855 -3578
- package/dist/index.js.map +1 -1
- package/dist/lib/cn.js +10 -1
- package/dist/lib/cn.js.map +1 -1
- package/package.json +21 -7
- package/src/__contract__/attribution-panel.contract.test.tsx +49 -0
- package/src/__contract__/badge.contract.test.tsx +49 -0
- package/src/__contract__/bounded-number.contract.test.tsx +49 -0
- package/src/__contract__/button.contract.test.tsx +49 -0
- package/src/__contract__/color-picker.contract.test.tsx +49 -0
- package/src/__contract__/combobox.contract.test.tsx +49 -0
- package/src/__contract__/command-trigger.contract.test.tsx +49 -0
- package/src/__contract__/copyable-value.contract.test.tsx +49 -0
- package/src/__contract__/date-picker.contract.test.tsx +49 -0
- package/src/__contract__/date-range-picker.contract.test.tsx +49 -0
- package/src/__contract__/empty-state.contract.test.tsx +49 -0
- package/src/__contract__/error-state.contract.test.tsx +49 -0
- package/src/__contract__/icon-button.contract.test.tsx +49 -0
- package/src/__contract__/input.contract.test.tsx +49 -0
- package/src/__contract__/key-value-editor.contract.test.tsx +49 -0
- package/src/__contract__/list-editor.contract.test.tsx +49 -0
- package/src/__contract__/loading-state.contract.test.tsx +49 -0
- package/src/__contract__/metric-card.contract.test.tsx +49 -0
- package/src/__contract__/model-picker.contract.test.tsx +49 -0
- package/src/__contract__/nav-notifications.contract.test.tsx +49 -0
- package/src/__contract__/number-input.contract.test.tsx +49 -0
- package/src/__contract__/rating.contract.test.tsx +49 -0
- package/src/__contract__/schema-form.contract.test.tsx +49 -0
- package/src/__contract__/section-header.contract.test.tsx +49 -0
- package/src/__contract__/segmented-field.contract.test.tsx +49 -0
- package/src/__contract__/slider-number.contract.test.tsx +49 -0
- package/src/__contract__/status-badge.contract.test.tsx +49 -0
- package/src/__contract__/tag-input.contract.test.tsx +49 -0
- package/src/__contract__/text.contract.test.tsx +49 -0
- package/src/__contract__/textarea.contract.test.tsx +49 -0
- package/src/__contract__/timeline.contract.test.tsx +49 -0
- package/src/__contract__/toggle.contract.test.tsx +49 -0
- package/src/__contract__/transfer.contract.test.tsx +49 -0
- package/src/__contract__/tree-select.contract.test.tsx +49 -0
- package/src/__contract__/tree.contract.test.tsx +49 -0
- package/src/__contract__/virtual-select.contract.test.tsx +49 -0
- package/src/__contract__/workspace-picker.contract.test.tsx +49 -0
- package/src/_peer-deps.test.ts +59 -0
- package/src/blocks-comparison-table.stories.tsx +2 -2
- package/src/components/accordion/accordion.tsx +13 -4
- package/src/components/advanced-group/advanced-group.tsx +1 -1
- package/src/components/alert/alert.tsx +15 -4
- package/src/components/alert-dialog/alert-dialog.test.tsx +32 -0
- package/src/components/alert-dialog/alert-dialog.tsx +28 -6
- package/src/components/app-shell/app-shell.stories.tsx +44 -2
- package/src/components/app-shell/app-shell.test.tsx +99 -0
- package/src/components/app-shell/app-shell.tsx +56 -4
- package/src/components/app-sidebar/app-sidebar.stories.tsx +9 -1
- package/src/components/attribution-panel/attribution-panel.tsx +3 -1
- package/src/components/attribution-panel/attributions.generated.ts +83 -79
- package/src/components/avatar/avatar.tsx +4 -1
- package/src/components/badge/badge.stories.tsx +8 -0
- package/src/components/badge/badge.test.tsx +8 -0
- package/src/components/badge/badge.tsx +15 -5
- package/src/components/bento-grid/bento-grid.test.tsx +24 -3
- package/src/components/bento-grid/bento-grid.tsx +11 -1
- package/src/components/bounded-number/bounded-number.test.tsx +1 -1
- package/src/components/bounded-number/bounded-number.tsx +78 -75
- package/src/components/breadcrumb/breadcrumb.stories.tsx +38 -0
- package/src/components/breadcrumb/breadcrumb.test.tsx +24 -1
- package/src/components/breadcrumb/breadcrumb.tsx +10 -4
- package/src/components/button/button.stories.tsx +116 -8
- package/src/components/button/button.tsx +18 -12
- package/src/components/button-group/button-group.tsx +1 -1
- package/src/components/calendar/calendar.tsx +3 -2
- package/src/components/card/card.stories.tsx +11 -6
- package/src/components/card/card.test.tsx +24 -2
- package/src/components/card/card.tsx +48 -7
- package/src/components/carousel/carousel.test.tsx +169 -0
- package/src/components/carousel/carousel.tsx +93 -32
- package/src/components/change-review/change-review.stories.tsx +145 -1
- package/src/components/change-review/change-review.test.tsx +143 -0
- package/src/components/change-review/change-review.tsx +214 -24
- package/src/components/checkbox/checkbox.tsx +2 -1
- package/src/components/collapsible-panel/use-collapsible-panel.ts +0 -3
- package/src/components/color-picker/color-picker.tsx +22 -15
- package/src/components/combobox/combobox.stories.tsx +10 -0
- package/src/components/combobox/combobox.test.tsx +54 -0
- package/src/components/combobox/combobox.tsx +25 -11
- package/src/components/command/command.stories.tsx +16 -6
- package/src/components/command/command.test.tsx +88 -0
- package/src/components/command/command.tsx +38 -13
- package/src/components/command/index.ts +1 -0
- package/src/components/command-trigger/command-trigger.stories.tsx +39 -0
- package/src/components/command-trigger/command-trigger.test.tsx +18 -0
- package/src/components/command-trigger/command-trigger.tsx +70 -0
- package/src/components/command-trigger/index.ts +1 -0
- package/src/components/context-menu/context-menu.tsx +16 -7
- package/src/components/context-rail/context-rail.stories.tsx +285 -0
- package/src/components/context-rail/context-rail.test.tsx +263 -0
- package/src/components/context-rail/context-rail.tsx +678 -0
- package/src/components/context-rail/index.ts +1 -0
- package/src/components/copyable-value/copyable-value.stories.tsx +10 -0
- package/src/components/copyable-value/copyable-value.test.tsx +28 -1
- package/src/components/copyable-value/copyable-value.tsx +13 -2
- package/src/components/date-picker/date-picker.stories.tsx +9 -0
- package/src/components/date-picker/date-picker.test.tsx +52 -0
- package/src/components/date-picker/date-picker.tsx +13 -15
- package/src/components/date-range-picker/date-range-picker.stories.tsx +9 -0
- package/src/components/date-range-picker/date-range-picker.test.tsx +76 -1
- package/src/components/date-range-picker/date-range-picker.tsx +39 -28
- package/src/components/descriptions/descriptions.tsx +1 -1
- package/src/components/dialog/dialog.test.tsx +8 -3
- package/src/components/dialog/dialog.tsx +14 -6
- package/src/components/drawer/drawer.tsx +23 -6
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +1 -1
- package/src/components/dropdown-menu/dropdown-menu.tsx +4 -4
- package/src/components/empty-state/empty-state.test.tsx +26 -0
- package/src/components/empty-state/empty-state.tsx +6 -1
- package/src/components/expand-dialog/expand-dialog.tsx +1 -2
- package/src/components/field/field-context.ts +59 -0
- package/src/components/field/field.stories.tsx +142 -0
- package/src/components/field/field.test.tsx +415 -0
- package/src/components/field/field.tsx +342 -0
- package/src/components/field/index.ts +9 -0
- package/src/components/field-row/field-row.stories.tsx +85 -1
- package/src/components/field-row/field-row.test.tsx +145 -0
- package/src/components/field-row/field-row.tsx +50 -6
- package/src/components/file-upload/file-upload.stories.tsx +16 -0
- package/src/components/file-upload/file-upload.test.tsx +90 -12
- package/src/components/file-upload/file-upload.tsx +164 -31
- package/src/components/form/form.tsx +8 -3
- package/src/components/hover-card/hover-card.tsx +1 -0
- package/src/components/icon-button/icon-button.test.tsx +1 -1
- package/src/components/icon-button/icon-button.tsx +3 -3
- package/src/components/input/input.stories.tsx +121 -0
- package/src/components/input/input.test.tsx +13 -0
- package/src/components/input/input.tsx +8 -4
- package/src/components/input-group/input-group.stories.tsx +74 -0
- package/src/components/input-group/input-group.tsx +18 -9
- package/src/components/input-otp/input-otp.tsx +8 -2
- package/src/components/keyboard-shortcuts/index.ts +6 -0
- package/src/components/keyboard-shortcuts/keyboard-shortcuts.stories.tsx +177 -0
- package/src/components/keyboard-shortcuts/keyboard-shortcuts.test.tsx +73 -0
- package/src/components/keyboard-shortcuts/keyboard-shortcuts.tsx +203 -0
- package/src/components/label/label.tsx +1 -1
- package/src/components/link-preview/link-preview.tsx +1 -1
- package/src/components/list-editor/list-editor.test.tsx +24 -0
- package/src/components/list-editor/list-editor.tsx +126 -52
- package/src/components/locale-provider/index.ts +6 -1
- package/src/components/locale-provider/locale-keys.test.ts +134 -0
- package/src/components/locale-provider/locale-provider.stories.tsx +89 -1
- package/src/components/locale-provider/locale-provider.test.tsx +257 -1
- package/src/components/locale-provider/locale-provider.tsx +63 -8
- package/src/components/locale-provider/messages.ts +822 -2
- package/src/components/match-highlight/match-highlight.stories.tsx +1 -1
- package/src/components/mention-input/mention-input-mirror.stories.tsx +9 -1
- package/src/components/mention-input/mention-input.stories.tsx +3 -3
- package/src/components/mention-input/mention-input.test.tsx +51 -1
- package/src/components/mention-input/mention-input.tsx +66 -46
- package/src/components/mention-input/mention-value.ts +8 -2
- package/src/components/menubar/menubar.tsx +16 -6
- package/src/components/metric-card/metric-card.stories.tsx +5 -3
- package/src/components/metric-card/metric-card.tsx +21 -2
- package/src/components/model-picker/model-picker.stories.tsx +63 -3
- package/src/components/model-picker/model-picker.test.tsx +63 -0
- package/src/components/model-picker/model-picker.tsx +62 -36
- package/src/components/nav-main/nav-main.tsx +8 -4
- package/src/components/nav-notifications/nav-notifications.stories.tsx +110 -0
- package/src/components/nav-notifications/nav-notifications.test.tsx +11 -0
- package/src/components/nav-notifications/nav-notifications.tsx +67 -9
- package/src/components/nav-user/nav-user.tsx +5 -5
- package/src/components/navigation-menu/navigation-menu.stories.tsx +22 -0
- package/src/components/navigation-menu/navigation-menu.test.tsx +174 -0
- package/src/components/navigation-menu/navigation-menu.tsx +204 -15
- package/src/components/number-input/number-input.stories.tsx +7 -3
- package/src/components/number-input/number-input.test.tsx +47 -0
- package/src/components/number-input/number-input.tsx +65 -24
- package/src/components/page-shell/page-shell.stories.tsx +163 -0
- package/src/components/page-shell/page-shell.test.tsx +229 -0
- package/src/components/page-shell/page-shell.tsx +108 -25
- package/src/components/pagination/pagination.test.tsx +109 -0
- package/src/components/pagination/pagination.tsx +27 -19
- package/src/components/popover/popover.tsx +1 -0
- package/src/components/progress/progress.tsx +2 -0
- package/src/components/radio-group/radio-group.tsx +1 -1
- package/src/components/rating/rating.tsx +3 -1
- package/src/components/resizable/resizable.stories.tsx +114 -2
- package/src/components/resizable/resizable.test.tsx +76 -0
- package/src/components/resizable/resizable.tsx +32 -6
- package/src/components/revision-timeline/revision-timeline.stories.tsx +8 -1
- package/src/components/revision-timeline/revision-timeline.test.tsx +26 -0
- package/src/components/revision-timeline/revision-timeline.tsx +53 -5
- package/src/components/schema-form/from-json-schema.test.ts +390 -0
- package/src/components/schema-form/from-json-schema.ts +375 -0
- package/src/components/schema-form/index.ts +81 -0
- package/src/components/schema-form/schema-form-spec.ts +698 -0
- package/src/components/schema-form/schema-form-store.ts +506 -0
- package/src/components/schema-form/schema-form.stories.tsx +566 -0
- package/src/components/schema-form/schema-form.test.tsx +1021 -0
- package/src/components/schema-form/schema-form.tsx +1381 -0
- package/src/components/section-header/section-header.stories.tsx +37 -0
- package/src/components/section-header/section-header.tsx +14 -3
- package/src/components/select/select.tsx +13 -7
- package/src/components/separator/separator.tsx +1 -0
- package/src/components/sheet/sheet.test.tsx +39 -0
- package/src/components/sheet/sheet.tsx +40 -9
- package/src/components/side-dock/index.ts +1 -0
- package/src/components/side-dock/side-dock.stories.tsx +137 -0
- package/src/components/side-dock/side-dock.test.tsx +299 -0
- package/src/components/side-dock/side-dock.tsx +515 -0
- package/src/components/sidebar/index.ts +1 -0
- package/src/components/sidebar/sidebar-frame.test.tsx +162 -0
- package/src/components/sidebar/sidebar.stories.tsx +421 -1
- package/src/components/sidebar/sidebar.test.tsx +71 -2
- package/src/components/sidebar/sidebar.tsx +264 -38
- package/src/components/skeleton/skeleton.tsx +1 -0
- package/src/components/skip-link/index.ts +1 -0
- package/src/components/skip-link/skip-link.stories.tsx +106 -0
- package/src/components/skip-link/skip-link.test.tsx +20 -0
- package/src/components/skip-link/skip-link.tsx +34 -0
- package/src/components/slider/slider.tsx +1 -1
- package/src/components/slider-number/slider-number.stories.tsx +4 -1
- package/src/components/slider-number/slider-number.test.tsx +1 -1
- package/src/components/sonner/sonner.tsx +2 -0
- package/src/components/spinner/spinner.test.tsx +18 -0
- package/src/components/spinner/spinner.tsx +14 -4
- package/src/components/split-panel/index.ts +6 -1
- package/src/components/split-panel/split-panel.stories.tsx +18 -2
- package/src/components/split-panel/split-panel.test.tsx +20 -0
- package/src/components/split-panel/split-panel.tsx +28 -8
- package/src/components/state-panel/state-panel.stories.tsx +52 -0
- package/src/components/state-panel/state-panel.test.tsx +125 -0
- package/src/components/state-panel/state-panel.tsx +110 -24
- package/src/components/status-badge/status-badge.stories.tsx +8 -0
- package/src/components/switch/switch.tsx +3 -1
- package/src/components/table/table.stories.tsx +64 -0
- package/src/components/table/table.test.tsx +149 -1
- package/src/components/table/table.tsx +71 -2
- package/src/components/tabs/index.ts +10 -1
- package/src/components/tabs/tabs.stories.tsx +48 -0
- package/src/components/tabs/tabs.test.tsx +67 -0
- package/src/components/tabs/tabs.tsx +176 -72
- package/src/components/tag-input/tag-input.tsx +6 -3
- package/src/components/team-switcher/team-switcher.tsx +35 -6
- package/src/components/textarea/textarea.tsx +2 -2
- package/src/components/theme-switcher/theme-switcher.stories.tsx +76 -0
- package/src/components/theme-switcher/theme-switcher.test.tsx +182 -0
- package/src/components/theme-switcher/theme-switcher.tsx +140 -12
- package/src/components/theme-switcher/use-theme-transition.ts +2 -2
- package/src/components/timeline/timeline.stories.tsx +27 -0
- package/src/components/timeline/timeline.tsx +10 -3
- package/src/components/toggle/toggle.tsx +7 -2
- package/src/components/toggle-group/toggle-group.tsx +2 -0
- package/src/components/toolbar/toolbar.stories.tsx +10 -2
- package/src/components/tooltip/tooltip.tsx +2 -1
- package/src/components/top-nav/top-nav.tsx +1 -1
- package/src/components/transfer/transfer.tsx +35 -30
- package/src/components/tree/tree.stories.tsx +2 -2
- package/src/components/tree/tree.test.tsx +41 -0
- package/src/components/tree/tree.tsx +91 -31
- package/src/components/tree/use-tree-keyboard.ts +37 -14
- package/src/components/tree-select/tree-select.stories.tsx +9 -0
- package/src/components/tree-select/tree-select.test.tsx +5 -0
- package/src/components/tree-select/tree-select.tsx +11 -14
- package/src/components/typography/prose.test.tsx +3 -2
- package/src/components/typography/prose.tsx +3 -3
- package/src/components/typography/typography.stories.tsx +228 -0
- package/src/components/view-toolbar/view-toolbar.stories.tsx +41 -1
- package/src/components/view-toolbar/view-toolbar.test.tsx +33 -0
- package/src/components/view-toolbar/view-toolbar.tsx +37 -4
- package/src/components/virtual-select/virtual-select.stories.tsx +9 -0
- package/src/components/virtual-select/virtual-select.test.tsx +29 -3
- package/src/components/virtual-select/virtual-select.tsx +56 -18
- package/src/components/wizard/wizard.test.tsx +9 -0
- package/src/components/wizard/wizard.tsx +13 -5
- package/src/components/workspace-picker/index.ts +3 -0
- package/src/components/workspace-picker/workspace-picker-state.ts +50 -0
- package/src/components/workspace-picker/workspace-picker.stories.tsx +126 -0
- package/src/components/workspace-picker/workspace-picker.test.tsx +207 -0
- package/src/components/workspace-picker/workspace-picker.tsx +172 -0
- package/src/illustrations/empty-list-illustration.tsx +26 -0
- package/src/illustrations/error-illustration.tsx +60 -0
- package/src/illustrations/first-run-illustration.tsx +27 -0
- package/src/illustrations/illustration-base.tsx +108 -0
- package/src/illustrations/illustrations.stories.tsx +94 -0
- package/src/illustrations/illustrations.test.tsx +98 -0
- package/src/illustrations/index.ts +16 -0
- package/src/illustrations/no-access-illustration.tsx +45 -0
- package/src/illustrations/no-results-illustration.tsx +46 -0
- package/src/illustrations/offline-illustration.tsx +37 -0
- package/src/illustrations/success-illustration.tsx +40 -0
- package/src/index.ts +80 -2
- package/src/lib/agent-event-model.test.ts +12 -0
- package/src/lib/agent-event-model.ts +42 -0
- package/src/lib/approval-option.test.ts +17 -0
- package/src/lib/approval-option.ts +53 -0
- package/src/lib/check-result.ts +33 -0
- package/src/lib/cn.ts +9 -0
- package/src/lib/csv.ts +48 -0
- package/src/lib/diff-line.test.ts +36 -0
- package/src/lib/diff-line.ts +63 -0
- package/src/lib/diff-rows.test.ts +76 -0
- package/src/lib/diff-rows.ts +94 -0
- package/src/lib/format-duration.test.ts +22 -0
- package/src/lib/format-duration.ts +30 -0
- package/src/lib/has-renderable-content.test.ts +49 -0
- package/src/lib/has-renderable-content.ts +32 -0
- package/src/lib/merge-refs.ts +2 -0
- package/src/lib/operating-mode.test.ts +36 -0
- package/src/lib/operating-mode.ts +81 -0
- package/src/lib/optional-peer.ts +59 -0
- package/src/lib/session-launch.ts +37 -0
- package/src/lib/slash-command.test.ts +44 -0
- package/src/lib/slash-command.ts +45 -0
- package/src/lib/trigger-query.test.ts +94 -0
- package/src/lib/trigger-query.ts +88 -0
- package/src/lib/use-controllable-state.ts +47 -0
- package/src/lib/use-mobile.test.tsx +47 -0
- package/src/lib/use-mobile.ts +34 -12
- package/src/motion.stories.tsx +6 -0
- package/src/templates-object-detail-hub.stories.tsx +2 -2
- package/src/templates-screen-states.stories.tsx +5 -5
- package/src/templates-settings.stories.tsx +1 -1
- package/src/blocks/sidebar-02/app-sidebar.tsx +0 -160
- package/src/blocks/sidebar-02/logo.tsx +0 -18
- package/src/blocks/sidebar-02/nav-main.tsx +0 -13
- package/src/blocks/sidebar-02/nav-notifications.tsx +0 -7
- package/src/blocks/sidebar-02/sidebar-02.stories.tsx +0 -23
- package/src/blocks/sidebar-02/team-switcher.tsx +0 -7
- package/src/blocks/sidebar-04/app-sidebar.tsx +0 -236
- package/src/blocks/sidebar-04/mail-context.tsx +0 -29
- package/src/blocks/sidebar-04/nav-user.tsx +0 -6
- package/src/blocks/sidebar-04/sidebar-04.stories.tsx +0 -150
- package/src/blocks/sidebar-05/app-sidebar.tsx +0 -375
- package/src/blocks/sidebar-05/sidebar-05.stories.tsx +0 -18
- package/src/blocks/sidebar-05/team-switcher.tsx +0 -6
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
/** sidebar-04 — double-sided mail sidebar (copy-owned block). Adapted from
|
|
2
|
-
* blocks.so (ephraim duncan). Tabler icons mapped to lucide. */
|
|
3
|
-
"use client";
|
|
4
|
-
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
import { Archive, Box, Clock, Flag, Inbox, Star } from "lucide-react";
|
|
7
|
-
import {
|
|
8
|
-
Sidebar,
|
|
9
|
-
SidebarContent,
|
|
10
|
-
SidebarFooter,
|
|
11
|
-
SidebarGroup,
|
|
12
|
-
SidebarGroupContent,
|
|
13
|
-
SidebarGroupLabel,
|
|
14
|
-
SidebarHeader,
|
|
15
|
-
SidebarInput,
|
|
16
|
-
SidebarMenu,
|
|
17
|
-
SidebarMenuButton,
|
|
18
|
-
SidebarMenuItem,
|
|
19
|
-
useSidebar,
|
|
20
|
-
} from "@elabs-ai/components-ui";
|
|
21
|
-
import { NavUser } from "@elabs-ai/components-ui";
|
|
22
|
-
import { useMail } from "./mail-context";
|
|
23
|
-
|
|
24
|
-
const data = {
|
|
25
|
-
user: { name: "ephraim", email: "ephraim@blocks.so", avatar: "" },
|
|
26
|
-
navMain: [
|
|
27
|
-
{ title: "Inbox", url: "#", icon: Inbox, isActive: true },
|
|
28
|
-
{ title: "Starred", url: "#", icon: Star, isActive: false },
|
|
29
|
-
{ title: "Important", url: "#", icon: Flag, isActive: false },
|
|
30
|
-
{ title: "Scheduled", url: "#", icon: Clock, isActive: false },
|
|
31
|
-
{ title: "Archive", url: "#", icon: Archive, isActive: false },
|
|
32
|
-
],
|
|
33
|
-
labels: [
|
|
34
|
-
{ title: "Personal", color: "bg-green-400 dark:bg-green-300" },
|
|
35
|
-
{ title: "Work", color: "bg-blue-400 dark:bg-blue-300" },
|
|
36
|
-
{ title: "Travel", color: "bg-orange-400 dark:bg-orange-300" },
|
|
37
|
-
{ title: "Receipts", color: "bg-purple-400 dark:bg-purple-300" },
|
|
38
|
-
],
|
|
39
|
-
mails: [
|
|
40
|
-
{
|
|
41
|
-
name: "Nora Patel",
|
|
42
|
-
email: "nora@acme.co",
|
|
43
|
-
subject: "Welcome to Acme Mail",
|
|
44
|
-
date: "08:15 AM",
|
|
45
|
-
teaser:
|
|
46
|
-
"Hi there — here's a quick tour of your new inbox.\nPin, label, and schedule messages to stay organized.",
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: "Stripe",
|
|
50
|
-
email: "no-reply@stripe.com",
|
|
51
|
-
subject: "Your payout has arrived",
|
|
52
|
-
date: "Yesterday",
|
|
53
|
-
teaser:
|
|
54
|
-
"A payout of $3,245.90 was sent to your bank account.\nView details in your dashboard.",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: "GitHub",
|
|
58
|
-
email: "noreply@github.com",
|
|
59
|
-
subject: "New activity on acme/app",
|
|
60
|
-
date: "Tue",
|
|
61
|
-
teaser:
|
|
62
|
-
"3 pull requests need your review. CI passed on main.\nClick to open the review queue.",
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: "Ava Chen",
|
|
66
|
-
email: "ava.chen@example.com",
|
|
67
|
-
subject: "Agenda for Friday standup",
|
|
68
|
-
date: "Mon",
|
|
69
|
-
teaser:
|
|
70
|
-
"Let's cover onboarding, billing bugs, and Q4 goals.\nReply with anything you want to add.",
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: "Figma",
|
|
74
|
-
email: "updates@figma.com",
|
|
75
|
-
subject: "What's new in Figma",
|
|
76
|
-
date: "Sep 12",
|
|
77
|
-
teaser:
|
|
78
|
-
"Variables, auto layout improvements, and dev mode updates.\nWatch the recap to learn more.",
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: "Linear",
|
|
82
|
-
email: "bot@linear.app",
|
|
83
|
-
subject: "[ACME-432] Edit modal broken",
|
|
84
|
-
date: "Sep 11",
|
|
85
|
-
teaser: "Issue created by Wendy. Repro steps included.\nSeverity: high, priority: P1.",
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: "Airbnb",
|
|
89
|
-
email: "booking@airbnb.com",
|
|
90
|
-
subject: "Your trip to Kyoto",
|
|
91
|
-
date: "Sep 10",
|
|
92
|
-
teaser: "Get ready for your stay. Check-in details and local recommendations inside.",
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
name: "Notion",
|
|
96
|
-
email: "team@notion.so",
|
|
97
|
-
subject: "Weekly team recap",
|
|
98
|
-
date: "Sep 09",
|
|
99
|
-
teaser:
|
|
100
|
-
"Marketing shipped pricing page revamp. Eng closed 14 issues.\nSee full notes in the doc.",
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|
106
|
-
const [activeItem, setActiveItem] = React.useState(data.navMain[0]);
|
|
107
|
-
const [mails, setMails] = React.useState(data.mails);
|
|
108
|
-
const [query, setQuery] = React.useState("");
|
|
109
|
-
const { setOpen } = useSidebar();
|
|
110
|
-
const { setSelectedMail } = useMail();
|
|
111
|
-
|
|
112
|
-
const filteredMails = React.useMemo(() => {
|
|
113
|
-
const q = query.trim().toLowerCase();
|
|
114
|
-
if (!q) return mails;
|
|
115
|
-
return mails.filter((m) =>
|
|
116
|
-
[m.name, m.email, m.subject, m.teaser].join("\n").toLowerCase().includes(q),
|
|
117
|
-
);
|
|
118
|
-
}, [mails, query]);
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
<div className="flex" {...props}>
|
|
122
|
-
{/* Icon rail */}
|
|
123
|
-
<Sidebar
|
|
124
|
-
style={{ "--sidebar-width": "12rem" } as React.CSSProperties}
|
|
125
|
-
collapsible="none"
|
|
126
|
-
className="border-e p-2 px-1"
|
|
127
|
-
>
|
|
128
|
-
<SidebarHeader>
|
|
129
|
-
<SidebarMenu>
|
|
130
|
-
<SidebarMenuItem>
|
|
131
|
-
<SidebarMenuButton size="lg" asChild className="md:h-8 md:p-0">
|
|
132
|
-
<a href="#">
|
|
133
|
-
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-accent text-sidebar-accent-foreground">
|
|
134
|
-
<Box className="size-4" />
|
|
135
|
-
</div>
|
|
136
|
-
<div className="grid flex-1 text-start text-body leading-tight">
|
|
137
|
-
<span className="truncate font-medium">Acme Inc</span>
|
|
138
|
-
<span className="truncate text-meta">Enterprise</span>
|
|
139
|
-
</div>
|
|
140
|
-
</a>
|
|
141
|
-
</SidebarMenuButton>
|
|
142
|
-
</SidebarMenuItem>
|
|
143
|
-
</SidebarMenu>
|
|
144
|
-
</SidebarHeader>
|
|
145
|
-
<SidebarContent>
|
|
146
|
-
<SidebarGroup>
|
|
147
|
-
<SidebarGroupContent className="px-1.5 md:px-0">
|
|
148
|
-
<SidebarMenu>
|
|
149
|
-
{data.navMain.map((item) => (
|
|
150
|
-
<SidebarMenuItem key={item.title}>
|
|
151
|
-
<SidebarMenuButton
|
|
152
|
-
onClick={() => {
|
|
153
|
-
setActiveItem(item);
|
|
154
|
-
const shuffled = [...data.mails].sort(() => Math.random() - 0.5);
|
|
155
|
-
setMails(shuffled.slice(0, Math.max(5, Math.floor(Math.random() * 8) + 1)));
|
|
156
|
-
setOpen(true);
|
|
157
|
-
}}
|
|
158
|
-
isActive={activeItem?.title === item.title}
|
|
159
|
-
className="px-2.5 md:px-2"
|
|
160
|
-
>
|
|
161
|
-
<item.icon />
|
|
162
|
-
<span>{item.title}</span>
|
|
163
|
-
</SidebarMenuButton>
|
|
164
|
-
</SidebarMenuItem>
|
|
165
|
-
))}
|
|
166
|
-
</SidebarMenu>
|
|
167
|
-
</SidebarGroupContent>
|
|
168
|
-
</SidebarGroup>
|
|
169
|
-
<SidebarGroup>
|
|
170
|
-
<SidebarGroupLabel className="px-1.5 text-meta md:px-0">Labels</SidebarGroupLabel>
|
|
171
|
-
<SidebarGroupContent className="px-1.5 md:px-0">
|
|
172
|
-
<SidebarMenu>
|
|
173
|
-
{data.labels.map((label) => (
|
|
174
|
-
<SidebarMenuItem key={label.title}>
|
|
175
|
-
<SidebarMenuButton asChild className="px-2.5 md:px-2">
|
|
176
|
-
<div className="flex items-center gap-3">
|
|
177
|
-
<div className={`h-3 w-3 rounded ${label.color}`} />
|
|
178
|
-
<span>{label.title}</span>
|
|
179
|
-
</div>
|
|
180
|
-
</SidebarMenuButton>
|
|
181
|
-
</SidebarMenuItem>
|
|
182
|
-
))}
|
|
183
|
-
</SidebarMenu>
|
|
184
|
-
</SidebarGroupContent>
|
|
185
|
-
</SidebarGroup>
|
|
186
|
-
</SidebarContent>
|
|
187
|
-
<SidebarFooter>
|
|
188
|
-
<NavUser user={data.user} />
|
|
189
|
-
</SidebarFooter>
|
|
190
|
-
</Sidebar>
|
|
191
|
-
|
|
192
|
-
{/* Mail list */}
|
|
193
|
-
<Sidebar collapsible="none" className="hidden min-w-96 flex-1 border-e md:flex">
|
|
194
|
-
<SidebarHeader className="gap-3.5 border-b p-4">
|
|
195
|
-
<div className="flex w-full items-center justify-between">
|
|
196
|
-
<div className="text-subtitle font-medium text-foreground">{activeItem?.title}</div>
|
|
197
|
-
</div>
|
|
198
|
-
<SidebarInput
|
|
199
|
-
placeholder="Type to search..."
|
|
200
|
-
value={query}
|
|
201
|
-
onChange={(e) => setQuery(e.target.value)}
|
|
202
|
-
/>
|
|
203
|
-
</SidebarHeader>
|
|
204
|
-
<SidebarContent>
|
|
205
|
-
<SidebarGroup className="px-0 pt-0">
|
|
206
|
-
<SidebarGroupContent>
|
|
207
|
-
{filteredMails.length === 0 && (
|
|
208
|
-
<div className="p-4 text-body text-muted-foreground">No results</div>
|
|
209
|
-
)}
|
|
210
|
-
{filteredMails.map((mail) => (
|
|
211
|
-
<a
|
|
212
|
-
href="#"
|
|
213
|
-
key={mail.email}
|
|
214
|
-
onClick={(e) => {
|
|
215
|
-
e.preventDefault();
|
|
216
|
-
setSelectedMail(mail);
|
|
217
|
-
}}
|
|
218
|
-
className="flex flex-col items-start gap-2 whitespace-nowrap border-b p-4 text-body leading-tight hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
|
|
219
|
-
>
|
|
220
|
-
<div className="flex w-full items-center gap-2">
|
|
221
|
-
<span>{mail.name}</span>
|
|
222
|
-
<span className="ms-auto text-meta">{mail.date}</span>
|
|
223
|
-
</div>
|
|
224
|
-
<span className="font-medium">{mail.subject}</span>
|
|
225
|
-
<span className="line-clamp-2 w-[260px] whitespace-break-spaces text-meta">
|
|
226
|
-
{mail.teaser}
|
|
227
|
-
</span>
|
|
228
|
-
</a>
|
|
229
|
-
))}
|
|
230
|
-
</SidebarGroupContent>
|
|
231
|
-
</SidebarGroup>
|
|
232
|
-
</SidebarContent>
|
|
233
|
-
</Sidebar>
|
|
234
|
-
</div>
|
|
235
|
-
);
|
|
236
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/** sidebar-04 — mail selection context (copy-owned block). */
|
|
2
|
-
"use client";
|
|
3
|
-
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
|
|
6
|
-
export type Mail = {
|
|
7
|
-
name: string;
|
|
8
|
-
email: string;
|
|
9
|
-
subject: string;
|
|
10
|
-
date: string;
|
|
11
|
-
teaser: string;
|
|
12
|
-
labels?: string[];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type MailContextValue = { selectedMail: Mail | null; setSelectedMail: (mail: Mail | null) => void };
|
|
16
|
-
|
|
17
|
-
const MailContext = React.createContext<MailContextValue | undefined>(undefined);
|
|
18
|
-
|
|
19
|
-
export function MailProvider({ children }: { children: React.ReactNode }) {
|
|
20
|
-
const [selectedMail, setSelectedMail] = React.useState<Mail | null>(null);
|
|
21
|
-
const value = React.useMemo(() => ({ selectedMail, setSelectedMail }), [selectedMail]);
|
|
22
|
-
return <MailContext.Provider value={value}>{children}</MailContext.Provider>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function useMail() {
|
|
26
|
-
const ctx = React.useContext(MailContext);
|
|
27
|
-
if (!ctx) throw new Error("useMail must be used within MailProvider");
|
|
28
|
-
return ctx;
|
|
29
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sidebar-04 — account menu. The shared `NavUser` nav primitive lives in
|
|
3
|
-
* `@elabs-ai/components-ui` (#99); this block re-exports it rather than forking a local copy
|
|
4
|
-
* (which drifts). The block's `app-sidebar.tsx` already imports it from `@elabs-ai/components-ui`.
|
|
5
|
-
*/
|
|
6
|
-
export { NavUser } from "@elabs-ai/components-ui";
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { expect, waitFor } from "storybook/test";
|
|
3
|
-
import { SidebarInset, SidebarProvider } from "@elabs-ai/components-ui";
|
|
4
|
-
import { AppSidebar } from "./app-sidebar";
|
|
5
|
-
import { MailProvider, useMail } from "./mail-context";
|
|
6
|
-
|
|
7
|
-
function MailPreview() {
|
|
8
|
-
const { selectedMail } = useMail();
|
|
9
|
-
if (!selectedMail) {
|
|
10
|
-
return (
|
|
11
|
-
<div className="flex h-full items-center justify-center text-body text-muted-foreground">
|
|
12
|
-
Select an email to preview
|
|
13
|
-
</div>
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
return (
|
|
17
|
-
<div className="flex h-full flex-col">
|
|
18
|
-
<div className="border-b p-4">
|
|
19
|
-
<div className="text-title font-semibold">{selectedMail.subject}</div>
|
|
20
|
-
<div className="text-body text-muted-foreground">
|
|
21
|
-
From: {selectedMail.name} ({selectedMail.email})
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
<div className="whitespace-pre-wrap p-4 text-body">{`${selectedMail.teaser}\n\nThis is a sample message body for the selected email.`}</div>
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const meta = {
|
|
30
|
-
title: "Layout/App Shell/Mail",
|
|
31
|
-
parameters: { layout: "fullscreen" },
|
|
32
|
-
} satisfies Meta;
|
|
33
|
-
export default meta;
|
|
34
|
-
type Story = StoryObj<typeof meta>;
|
|
35
|
-
|
|
36
|
-
export const Default: Story = {
|
|
37
|
-
render: () => (
|
|
38
|
-
<SidebarProvider>
|
|
39
|
-
<MailProvider>
|
|
40
|
-
<div className="flex h-dvh w-full">
|
|
41
|
-
<AppSidebar />
|
|
42
|
-
<SidebarInset>
|
|
43
|
-
<div className="m-2 flex-1 rounded-xl border">
|
|
44
|
-
<MailPreview />
|
|
45
|
-
</div>
|
|
46
|
-
</SidebarInset>
|
|
47
|
-
</div>
|
|
48
|
-
</MailProvider>
|
|
49
|
-
</SidebarProvider>
|
|
50
|
-
),
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* #397: `layout-app-shell-mail--default` was the worst-measured real screen
|
|
55
|
-
* for the density type dial (10 of 49 scaling text nodes) because
|
|
56
|
-
* `app-sidebar.tsx`'s 8 raw `text‑sm`/`text‑xs`/`text‑base` utilities were
|
|
57
|
-
* immune to `data-density` — see `.claude/rules/styling-and-tokens.md` "Type
|
|
58
|
-
* is a role, not a size". All 8 now read a `text-<role>` utility instead, so
|
|
59
|
-
* every one of these text nodes scales. Two columns pin `data-density` on a
|
|
60
|
-
* plain wrapping div — NOT `<ThemeProvider>`, which writes `data-density` to
|
|
61
|
-
* `document.documentElement` and would race two columns — mirroring
|
|
62
|
-
* `Foundations/Typography → Density scale`'s pattern. Representative sample:
|
|
63
|
-
* one node per converted role class is enough, since every site sharing a
|
|
64
|
-
* role resolves to the identical computed style.
|
|
65
|
-
*/
|
|
66
|
-
export const DensityComparison: Story = {
|
|
67
|
-
name: "Density comparison (#397)",
|
|
68
|
-
parameters: {
|
|
69
|
-
docs: {
|
|
70
|
-
description: {
|
|
71
|
-
story:
|
|
72
|
-
"The 8 raw `text‑sm`/`text‑xs`/`text‑base` sites in `app-sidebar.tsx` " +
|
|
73
|
-
"now read `text-body`/`text-meta`/`text-subtitle`, so they scale with " +
|
|
74
|
-
"`data-density` like the rest of the shell. `text‑sm` → `text-body` " +
|
|
75
|
-
"and `text‑base` → `text-subtitle` are documented no-ops (identical " +
|
|
76
|
-
"size + line-height); `text‑xs` → `text-meta` also adopts " +
|
|
77
|
-
"`font-weight: 500` + `letter-spacing: 0.01em` (a real, deliberate " +
|
|
78
|
-
"shift, not a no-op).",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
render: () => (
|
|
83
|
-
<div className="flex gap-8">
|
|
84
|
-
{(["compact", "comfortable", "spacious"] as const).map((mode) => (
|
|
85
|
-
<div data-density={mode} data-testid={`density-${mode}`} key={mode}>
|
|
86
|
-
<p className="mb-2 text-caption text-muted-foreground">{mode}</p>
|
|
87
|
-
<div className="flex h-[420px] w-[520px] overflow-hidden rounded-lg border">
|
|
88
|
-
<SidebarProvider>
|
|
89
|
-
<MailProvider>
|
|
90
|
-
<AppSidebar />
|
|
91
|
-
</MailProvider>
|
|
92
|
-
</SidebarProvider>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
))}
|
|
96
|
-
</div>
|
|
97
|
-
),
|
|
98
|
-
play: async ({ canvasElement }) => {
|
|
99
|
-
const px = (el: Element) => parseFloat(getComputedStyle(el).fontSize);
|
|
100
|
-
const compact = canvasElement.querySelector('[data-testid="density-compact"]');
|
|
101
|
-
const comfortable = canvasElement.querySelector('[data-testid="density-comfortable"]');
|
|
102
|
-
const spacious = canvasElement.querySelector('[data-testid="density-spacious"]');
|
|
103
|
-
expect(compact).not.toBeNull();
|
|
104
|
-
expect(comfortable).not.toBeNull();
|
|
105
|
-
expect(spacious).not.toBeNull();
|
|
106
|
-
|
|
107
|
-
// `text‑xs` → `text-meta` sites ("Enterprise", "Labels", mail date, mail
|
|
108
|
-
// teaser): 11.25px compact / 12px comfortable / 12.75px spacious,
|
|
109
|
-
// matching Gantt's timescale.
|
|
110
|
-
await waitFor(() => {
|
|
111
|
-
expect(compact?.querySelector(".text-meta")).not.toBeNull();
|
|
112
|
-
expect(comfortable?.querySelector(".text-meta")).not.toBeNull();
|
|
113
|
-
expect(spacious?.querySelector(".text-meta")).not.toBeNull();
|
|
114
|
-
});
|
|
115
|
-
const metaCompact = compact?.querySelector(".text-meta") as Element;
|
|
116
|
-
const metaComfortable = comfortable?.querySelector(".text-meta") as Element;
|
|
117
|
-
const metaSpacious = spacious?.querySelector(".text-meta") as Element;
|
|
118
|
-
expect(px(metaComfortable)).toBe(12);
|
|
119
|
-
expect(px(metaCompact)).toBe(11.25);
|
|
120
|
-
expect(px(metaSpacious)).toBe(12.75);
|
|
121
|
-
expect(px(metaCompact)).toBeLessThan(px(metaComfortable));
|
|
122
|
-
expect(px(metaSpacious)).toBeGreaterThan(px(metaComfortable));
|
|
123
|
-
|
|
124
|
-
// `text‑sm` → `text-body` sites ("Acme Inc" wrapper, mail row): a
|
|
125
|
-
// documented visual no-op at `comfortable` (14px, identical to a pre-#397
|
|
126
|
-
// build), still scales at `compact` (13.125px) / `spacious` (14.875px).
|
|
127
|
-
const bodyCompact = compact?.querySelector(".text-body") as Element;
|
|
128
|
-
const bodyComfortable = comfortable?.querySelector(".text-body") as Element;
|
|
129
|
-
const bodySpacious = spacious?.querySelector(".text-body") as Element;
|
|
130
|
-
expect(bodyCompact).not.toBeNull();
|
|
131
|
-
expect(bodyComfortable).not.toBeNull();
|
|
132
|
-
expect(bodySpacious).not.toBeNull();
|
|
133
|
-
expect(px(bodyComfortable)).toBe(14);
|
|
134
|
-
expect(px(bodyCompact)).toBe(13.125);
|
|
135
|
-
expect(px(bodySpacious)).toBe(14.875);
|
|
136
|
-
|
|
137
|
-
// `text‑base` → `text-subtitle` site (the mail-list header title, e.g.
|
|
138
|
-
// "Inbox"): 16px comfortable (identical to a pre-#397 `text‑base` build)
|
|
139
|
-
// / 15px compact / 17px spacious.
|
|
140
|
-
const subtitleCompact = compact?.querySelector(".text-subtitle") as Element;
|
|
141
|
-
const subtitleComfortable = comfortable?.querySelector(".text-subtitle") as Element;
|
|
142
|
-
const subtitleSpacious = spacious?.querySelector(".text-subtitle") as Element;
|
|
143
|
-
expect(subtitleCompact).not.toBeNull();
|
|
144
|
-
expect(subtitleComfortable).not.toBeNull();
|
|
145
|
-
expect(subtitleSpacious).not.toBeNull();
|
|
146
|
-
expect(px(subtitleComfortable)).toBe(16);
|
|
147
|
-
expect(px(subtitleCompact)).toBe(15);
|
|
148
|
-
expect(px(subtitleSpacious)).toBe(17);
|
|
149
|
-
},
|
|
150
|
-
};
|