@passui/react 0.0.0-stage → 0.1.15
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/LICENSE +21 -0
- package/README.md +83 -2
- package/package.json +96 -5
- package/src/appearance/index.ts +2 -0
- package/src/appearance/platform.tsx +123 -0
- package/src/appearance/theme.tsx +535 -0
- package/src/components/ambient/ambient.docs.tsx +68 -0
- package/src/components/ambient/ambient.tsx +43 -0
- package/src/components/ambient/examples/ambient-images.tsx +30 -0
- package/src/components/ambient/examples/ambient-now-playing.tsx +55 -0
- package/src/components/ambient/examples/ambient-showcase.tsx +33 -0
- package/src/components/ambient/examples/ambient-usage.tsx +19 -0
- package/src/components/ambient/index.ts +1 -0
- package/src/components/button/button-styles.ts +60 -0
- package/src/components/button/button.docs.tsx +116 -0
- package/src/components/button/button.tsx +91 -0
- package/src/components/button/examples/button-disabled.tsx +19 -0
- package/src/components/button/examples/button-icon.tsx +39 -0
- package/src/components/button/examples/button-link.tsx +12 -0
- package/src/components/button/examples/button-pending.tsx +27 -0
- package/src/components/button/examples/button-showcase.tsx +115 -0
- package/src/components/button/examples/button-sizes.tsx +12 -0
- package/src/components/button/examples/button-usage.tsx +12 -0
- package/src/components/button/examples/button-variants.tsx +15 -0
- package/src/components/button/examples/button-with-icon.tsx +32 -0
- package/src/components/button/examples/icon-button-player.tsx +38 -0
- package/src/components/button/examples/icon-button-showcase.tsx +83 -0
- package/src/components/button/examples/icon-button-usage.tsx +11 -0
- package/src/components/button/examples/icon-button-variants.tsx +39 -0
- package/src/components/button/icon-button.docs.tsx +68 -0
- package/src/components/button/icon-button.tsx +73 -0
- package/src/components/button/pending-spinner.tsx +14 -0
- package/src/components/button/use-pending-indicator.ts +27 -0
- package/src/components/chip/chip.docs.tsx +37 -0
- package/src/components/chip/chip.tsx +35 -0
- package/src/components/chip/examples/chip-in-rows.tsx +19 -0
- package/src/components/chip/examples/chip-showcase.tsx +51 -0
- package/src/components/chip/examples/chip-tones.tsx +13 -0
- package/src/components/chip/examples/chip-usage.tsx +5 -0
- package/src/components/compact-date-picker/calendar-month.ts +131 -0
- package/src/components/compact-date-picker/compact-date-picker.docs.tsx +81 -0
- package/src/components/compact-date-picker/compact-date-picker.tsx +438 -0
- package/src/components/compact-date-picker/examples/compact-date-picker-showcase.tsx +35 -0
- package/src/components/compact-date-picker/examples/compact-date-picker-usage.tsx +22 -0
- package/src/components/compact-time-picker/compact-time-picker.docs.tsx +80 -0
- package/src/components/compact-time-picker/compact-time-picker.tsx +432 -0
- package/src/components/compact-time-picker/examples/compact-time-picker-showcase.tsx +31 -0
- package/src/components/compact-time-picker/examples/compact-time-picker-usage.tsx +21 -0
- package/src/components/compact-time-picker/time-value.ts +67 -0
- package/src/components/content-view/content-view.docs.tsx +61 -0
- package/src/components/content-view/content-view.tsx +197 -0
- package/src/components/content-view/examples/content-view-header-action.tsx +33 -0
- package/src/components/content-view/examples/content-view-showcase.tsx +65 -0
- package/src/components/content-view/examples/content-view-stats.tsx +35 -0
- package/src/components/content-view/examples/content-view-usage.tsx +14 -0
- package/src/components/content-view/index.ts +1 -0
- package/src/components/context-menu/conceal-menu-source.ts +33 -0
- package/src/components/context-menu/context-menu-host.tsx +445 -0
- package/src/components/context-menu/context-menu-store.ts +165 -0
- package/src/components/context-menu/context-menu.docs.tsx +92 -0
- package/src/components/context-menu/examples/context-menu-checked.tsx +37 -0
- package/src/components/context-menu/examples/context-menu-list.tsx +85 -0
- package/src/components/context-menu/examples/context-menu-showcase.tsx +56 -0
- package/src/components/context-menu/examples/context-menu-toolbar.tsx +63 -0
- package/src/components/context-menu/examples/context-menu-usage.tsx +24 -0
- package/src/components/context-menu/menu-position.ts +55 -0
- package/src/components/context-menu/menu-reveal.ts +75 -0
- package/src/components/context-menu/use-context-menu.ts +90 -0
- package/src/components/conversation/conversation.docs.tsx +88 -0
- package/src/components/conversation/conversation.tsx +279 -0
- package/src/components/conversation/examples/conversation-composer.tsx +36 -0
- package/src/components/conversation/examples/conversation-days.tsx +48 -0
- package/src/components/conversation/examples/conversation-showcase.tsx +71 -0
- package/src/components/conversation/examples/conversation-usage.tsx +28 -0
- package/src/components/conversation/index.ts +1 -0
- package/src/components/date-picker/date-picker.docs.tsx +88 -0
- package/src/components/date-picker/date-picker.tsx +103 -0
- package/src/components/date-picker/date-value.ts +37 -0
- package/src/components/date-picker/examples/date-picker-locales.tsx +41 -0
- package/src/components/date-picker/examples/date-picker-past-only.tsx +29 -0
- package/src/components/date-picker/examples/date-picker-showcase.tsx +49 -0
- package/src/components/date-picker/examples/date-picker-usage.tsx +22 -0
- package/src/components/dialog/dialog-host.tsx +156 -0
- package/src/components/dialog/dialog-store.ts +56 -0
- package/src/components/dialog/dialog.docs.tsx +114 -0
- package/src/components/dialog/dialog.tsx +200 -0
- package/src/components/dialog/examples/dialog-confirm.tsx +24 -0
- package/src/components/dialog/examples/dialog-form.tsx +46 -0
- package/src/components/dialog/examples/dialog-inline.tsx +48 -0
- package/src/components/dialog/examples/dialog-showcase.tsx +53 -0
- package/src/components/dialog/examples/dialog-usage.tsx +31 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/labeled-content/examples/labeled-content-descriptions.tsx +37 -0
- package/src/components/labeled-content/examples/labeled-content-showcase.tsx +54 -0
- package/src/components/labeled-content/examples/labeled-content-usage.tsx +22 -0
- package/src/components/labeled-content/examples/labeled-content-values.tsx +26 -0
- package/src/components/labeled-content/examples/labeled-content-wide.tsx +46 -0
- package/src/components/labeled-content/labeled-content.docs.tsx +69 -0
- package/src/components/labeled-content/labeled-content.tsx +66 -0
- package/src/components/locale-switcher/examples/locale-switcher-native-names.tsx +30 -0
- package/src/components/locale-switcher/examples/locale-switcher-showcase.tsx +46 -0
- package/src/components/locale-switcher/examples/locale-switcher-translated-page.tsx +35 -0
- package/src/components/locale-switcher/examples/locale-switcher-usage.tsx +20 -0
- package/src/components/locale-switcher/index.ts +1 -0
- package/src/components/locale-switcher/locale-switcher.docs.tsx +91 -0
- package/src/components/locale-switcher/locale-switcher.tsx +252 -0
- package/src/components/picker/examples/picker-details.tsx +44 -0
- package/src/components/picker/examples/picker-searchable.tsx +48 -0
- package/src/components/picker/examples/picker-showcase.tsx +66 -0
- package/src/components/picker/examples/picker-usage.tsx +20 -0
- package/src/components/picker/picker.docs.tsx +68 -0
- package/src/components/picker/picker.tsx +493 -0
- package/src/components/popover/examples/popover-help.tsx +57 -0
- package/src/components/popover/examples/popover-menu.tsx +51 -0
- package/src/components/popover/examples/popover-showcase.tsx +61 -0
- package/src/components/popover/examples/popover-usage.tsx +31 -0
- package/src/components/popover/popover.docs.tsx +84 -0
- package/src/components/popover/popover.tsx +138 -0
- package/src/components/profile-image/examples/profile-image-fallbacks.tsx +29 -0
- package/src/components/profile-image/examples/profile-image-group.tsx +34 -0
- package/src/components/profile-image/examples/profile-image-showcase.tsx +30 -0
- package/src/components/profile-image/examples/profile-image-usage.tsx +14 -0
- package/src/components/profile-image/profile-image.docs.tsx +81 -0
- package/src/components/profile-image/profile-image.tsx +80 -0
- package/src/components/search-field/examples/search-field-filter.tsx +53 -0
- package/src/components/search-field/examples/search-field-showcase.tsx +56 -0
- package/src/components/search-field/examples/search-field-toolbar.tsx +27 -0
- package/src/components/search-field/examples/search-field-usage.tsx +18 -0
- package/src/components/search-field/search-field.docs.tsx +58 -0
- package/src/components/search-field/search-field.tsx +29 -0
- package/src/components/search-list/examples/search-list-dropdown.tsx +53 -0
- package/src/components/search-list/examples/search-list-groups.tsx +64 -0
- package/src/components/search-list/examples/search-list-showcase.tsx +67 -0
- package/src/components/search-list/examples/search-list-usage.tsx +27 -0
- package/src/components/search-list/search-list.docs.tsx +87 -0
- package/src/components/search-list/search-list.tsx +265 -0
- package/src/components/section/examples/section-actions.tsx +35 -0
- package/src/components/section/examples/section-groups.tsx +41 -0
- package/src/components/section/examples/section-showcase.tsx +63 -0
- package/src/components/section/examples/section-usage.tsx +19 -0
- package/src/components/section/section.docs.tsx +51 -0
- package/src/components/section/section.tsx +41 -0
- package/src/components/secure-field/examples/secure-field-new-password.tsx +56 -0
- package/src/components/secure-field/examples/secure-field-reveal.tsx +20 -0
- package/src/components/secure-field/examples/secure-field-showcase.tsx +62 -0
- package/src/components/secure-field/examples/secure-field-usage.tsx +19 -0
- package/src/components/secure-field/secure-field.docs.tsx +72 -0
- package/src/components/secure-field/secure-field.tsx +10 -0
- package/src/components/segmented-control/examples/segmented-control-overflow.tsx +28 -0
- package/src/components/segmented-control/examples/segmented-control-showcase.tsx +59 -0
- package/src/components/segmented-control/examples/segmented-control-usage.tsx +20 -0
- package/src/components/segmented-control/examples/segmented-control-views.tsx +32 -0
- package/src/components/segmented-control/segmented-control.docs.tsx +61 -0
- package/src/components/segmented-control/segmented-control.tsx +122 -0
- package/src/components/sheet/examples/sheet-choices.tsx +58 -0
- package/src/components/sheet/examples/sheet-share.tsx +51 -0
- package/src/components/sheet/examples/sheet-showcase.tsx +74 -0
- package/src/components/sheet/examples/sheet-usage.tsx +32 -0
- package/src/components/sheet/modal-environment.ts +113 -0
- package/src/components/sheet/sheet-portal.tsx +89 -0
- package/src/components/sheet/sheet.docs.tsx +75 -0
- package/src/components/sheet/sheet.tsx +112 -0
- package/src/components/sheet/use-sheet-focus.ts +130 -0
- package/src/components/sidebar/examples/sidebar-groups.tsx +133 -0
- package/src/components/sidebar/examples/sidebar-showcase.tsx +123 -0
- package/src/components/sidebar/examples/sidebar-unread.tsx +66 -0
- package/src/components/sidebar/examples/sidebar-usage.tsx +52 -0
- package/src/components/sidebar/sidebar.docs.tsx +135 -0
- package/src/components/sidebar/sidebar.tsx +69 -0
- package/src/components/stat-tile/examples/stat-tile-filters.tsx +32 -0
- package/src/components/stat-tile/examples/stat-tile-grid.tsx +21 -0
- package/src/components/stat-tile/examples/stat-tile-showcase.tsx +52 -0
- package/src/components/stat-tile/examples/stat-tile-usage.tsx +13 -0
- package/src/components/stat-tile/index.ts +1 -0
- package/src/components/stat-tile/stat-tile.docs.tsx +60 -0
- package/src/components/stat-tile/stat-tile.tsx +68 -0
- package/src/components/status-indicator/examples/status-indicator-live.tsx +34 -0
- package/src/components/status-indicator/examples/status-indicator-showcase.tsx +39 -0
- package/src/components/status-indicator/examples/status-indicator-states.tsx +17 -0
- package/src/components/status-indicator/examples/status-indicator-usage.tsx +9 -0
- package/src/components/status-indicator/status-indicator.docs.tsx +43 -0
- package/src/components/status-indicator/status-indicator.tsx +35 -0
- package/src/components/swatch-picker/examples/swatch-picker-accents.tsx +42 -0
- package/src/components/swatch-picker/examples/swatch-picker-labels.tsx +45 -0
- package/src/components/swatch-picker/examples/swatch-picker-showcase.tsx +70 -0
- package/src/components/swatch-picker/examples/swatch-picker-usage.tsx +39 -0
- package/src/components/swatch-picker/swatch-picker.docs.tsx +60 -0
- package/src/components/swatch-picker/swatch-picker.tsx +96 -0
- package/src/components/tab-bar/examples/tab-bar-five-tabs.tsx +66 -0
- package/src/components/tab-bar/examples/tab-bar-minimize.tsx +71 -0
- package/src/components/tab-bar/examples/tab-bar-showcase.tsx +82 -0
- package/src/components/tab-bar/examples/tab-bar-usage.tsx +54 -0
- package/src/components/tab-bar/tab-bar.docs.tsx +136 -0
- package/src/components/tab-bar/tab-bar.tsx +148 -0
- package/src/components/tab-view/examples/tab-view-keyboard.tsx +39 -0
- package/src/components/tab-view/examples/tab-view-settings.tsx +56 -0
- package/src/components/tab-view/examples/tab-view-showcase.tsx +53 -0
- package/src/components/tab-view/examples/tab-view-usage.tsx +23 -0
- package/src/components/tab-view/tab-view.docs.tsx +77 -0
- package/src/components/tab-view/tab-view.tsx +130 -0
- package/src/components/table/examples/table-empty.tsx +53 -0
- package/src/components/table/examples/table-invoices.tsx +102 -0
- package/src/components/table/examples/table-selection.tsx +150 -0
- package/src/components/table/examples/table-showcase.tsx +115 -0
- package/src/components/table/examples/table-usage.tsx +67 -0
- package/src/components/table/examples/table-wide.tsx +75 -0
- package/src/components/table/table.docs.tsx +131 -0
- package/src/components/table/table.tsx +149 -0
- package/src/components/text-editor/examples/text-editor-comment.tsx +48 -0
- package/src/components/text-editor/examples/text-editor-count.tsx +38 -0
- package/src/components/text-editor/examples/text-editor-showcase.tsx +58 -0
- package/src/components/text-editor/examples/text-editor-usage.tsx +25 -0
- package/src/components/text-editor/text-editor.docs.tsx +61 -0
- package/src/components/text-editor/text-editor.tsx +19 -0
- package/src/components/text-field/examples/text-field-icons.tsx +72 -0
- package/src/components/text-field/examples/text-field-showcase.tsx +50 -0
- package/src/components/text-field/examples/text-field-usage.tsx +18 -0
- package/src/components/text-field/examples/text-field-validation.tsx +27 -0
- package/src/components/text-field/text-field.docs.tsx +118 -0
- package/src/components/text-field/text-field.tsx +135 -0
- package/src/components/toast/examples/toast-action.tsx +32 -0
- package/src/components/toast/examples/toast-promise.tsx +45 -0
- package/src/components/toast/examples/toast-showcase.tsx +49 -0
- package/src/components/toast/examples/toast-usage.tsx +13 -0
- package/src/components/toast/toast-store.ts +166 -0
- package/src/components/toast/toast.docs.tsx +79 -0
- package/src/components/toast/toaster.tsx +374 -0
- package/src/components/toggle/examples/toggle-checkboxes.tsx +43 -0
- package/src/components/toggle/examples/toggle-settings.tsx +31 -0
- package/src/components/toggle/examples/toggle-showcase.tsx +59 -0
- package/src/components/toggle/examples/toggle-usage.tsx +13 -0
- package/src/components/toggle/toggle.docs.tsx +69 -0
- package/src/components/toggle/toggle.tsx +85 -0
- package/src/components/tooltip/examples/tooltip-icon-buttons.tsx +27 -0
- package/src/components/tooltip/examples/tooltip-shortcuts.tsx +38 -0
- package/src/components/tooltip/examples/tooltip-showcase.tsx +44 -0
- package/src/components/tooltip/examples/tooltip-usage.tsx +13 -0
- package/src/components/tooltip/tooltip-host.tsx +156 -0
- package/src/components/tooltip/tooltip.docs.tsx +56 -0
- package/src/components/view-controller/examples/view-controller-action.tsx +27 -0
- package/src/components/view-controller/examples/view-controller-settings.tsx +31 -0
- package/src/components/view-controller/examples/view-controller-showcase.tsx +45 -0
- package/src/components/view-controller/examples/view-controller-usage.tsx +19 -0
- package/src/components/view-controller/index.ts +1 -0
- package/src/components/view-controller/view-controller.docs.tsx +71 -0
- package/src/components/view-controller/view-controller.tsx +81 -0
- package/src/docs/index.ts +3 -0
- package/src/docs/registry.ts +113 -0
- package/src/docs/snippet.ts +17 -0
- package/src/docs/types.ts +180 -0
- package/src/hooks/scroll/minimize-decision.ts +13 -0
- package/src/hooks/use-anchored-placement.ts +43 -0
- package/src/hooks/use-dismiss.ts +58 -0
- package/src/hooks/use-media-query.ts +106 -0
- package/src/hooks/use-minimize-on-scroll.ts +92 -0
- package/src/hooks/use-visual-viewport.ts +76 -0
- package/src/hooks/viewport/types.ts +19 -0
- package/src/hooks/viewport/viewport-metrics.ts +25 -0
- package/src/icons/icons.tsx +298 -0
- package/src/index.ts +78 -0
- package/src/lib/anchored-placement.ts +92 -0
- package/src/lib/class-names.ts +21 -0
- package/src/lib/date.ts +96 -0
- package/src/lib/floating-layers.ts +28 -0
- package/src/lib/haptics.ts +25 -0
- package/src/lib/language.ts +21 -0
- package/src/lib/screen-edges.ts +45 -0
- package/src/lib/shapes.ts +70 -0
- package/src/motion/transitions.ts +30 -0
- package/src/styles/base.css +1 -0
- package/src/styles/styles.css +3 -0
- package/src/styles/theme.css +1 -0
- package/src/styles/typeset.css +1 -0
- package/src/styles/utilities.css +1 -0
- package/src/tokens.ts +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ViewController } from "@passui/react/view-controller"
|
|
2
|
+
|
|
3
|
+
export function ViewControllerUsage() {
|
|
4
|
+
return (
|
|
5
|
+
<ViewController
|
|
6
|
+
className="py-6"
|
|
7
|
+
heading={
|
|
8
|
+
<ViewController.Heading
|
|
9
|
+
title="Devices"
|
|
10
|
+
description="Where you're signed in right now."
|
|
11
|
+
/>
|
|
12
|
+
}
|
|
13
|
+
>
|
|
14
|
+
<p className="text-callout text-label-secondary">
|
|
15
|
+
The screen's content goes here.
|
|
16
|
+
</p>
|
|
17
|
+
</ViewController>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./view-controller"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ViewControllerSettings } from "./examples/view-controller-settings"
|
|
2
|
+
import { Button } from "../button/button"
|
|
3
|
+
import { jsxAttribute } from "../../docs/snippet"
|
|
4
|
+
import { defineDocs } from "../../docs/types"
|
|
5
|
+
import { ViewControllerAction } from "./examples/view-controller-action"
|
|
6
|
+
import { ViewControllerShowcase } from "./examples/view-controller-showcase"
|
|
7
|
+
import { ViewControllerUsage } from "./examples/view-controller-usage"
|
|
8
|
+
import { ViewController } from "./view-controller"
|
|
9
|
+
|
|
10
|
+
// The controls belong to ViewController.Heading, passed as `heading`;
|
|
11
|
+
// the snippet writes that element itself.
|
|
12
|
+
type DemoProps = {
|
|
13
|
+
title: string
|
|
14
|
+
description: string
|
|
15
|
+
size: "default" | "compact"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const viewControllerDocs = defineDocs<DemoProps>()({
|
|
19
|
+
slug: "view-controller",
|
|
20
|
+
name: "ViewController",
|
|
21
|
+
importPath: "@passui/react/view-controller",
|
|
22
|
+
controls: {
|
|
23
|
+
title: { kind: "text", default: "Security" },
|
|
24
|
+
description: {
|
|
25
|
+
kind: "text",
|
|
26
|
+
default:
|
|
27
|
+
"Choose how you sign in and which devices stay signed in.",
|
|
28
|
+
},
|
|
29
|
+
size: {
|
|
30
|
+
kind: "select",
|
|
31
|
+
options: { default: true, compact: true },
|
|
32
|
+
default: "default",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
render: ({ title, description, size }) => (
|
|
36
|
+
<ViewController
|
|
37
|
+
className="py-6"
|
|
38
|
+
heading={
|
|
39
|
+
<ViewController.Heading
|
|
40
|
+
title={title}
|
|
41
|
+
description={description || undefined}
|
|
42
|
+
size={size}
|
|
43
|
+
/>
|
|
44
|
+
}
|
|
45
|
+
>
|
|
46
|
+
<Button
|
|
47
|
+
variant="secondary"
|
|
48
|
+
className="self-start"
|
|
49
|
+
>
|
|
50
|
+
Change password
|
|
51
|
+
</Button>
|
|
52
|
+
</ViewController>
|
|
53
|
+
),
|
|
54
|
+
childrenSnippet: ({ title, description, size }) => ({
|
|
55
|
+
imports: [],
|
|
56
|
+
omit: ["title", "description", "size"],
|
|
57
|
+
expressionProps: {
|
|
58
|
+
heading: `<ViewController.Heading ${jsxAttribute("title", title)}${description ? ` ${jsxAttribute("description", description)}` : ""}${size === "compact" ? ' size="compact"' : ""} />`,
|
|
59
|
+
},
|
|
60
|
+
children: "\n {/* the screen's content */}\n",
|
|
61
|
+
}),
|
|
62
|
+
usage: ViewControllerUsage,
|
|
63
|
+
showcase: ViewControllerShowcase,
|
|
64
|
+
examples: [
|
|
65
|
+
{
|
|
66
|
+
id: "action",
|
|
67
|
+
component: ViewControllerAction,
|
|
68
|
+
},
|
|
69
|
+
{ id: "settings", component: ViewControllerSettings },
|
|
70
|
+
],
|
|
71
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { PropsWithChildren, ReactNode } from "react"
|
|
2
|
+
import { cn } from "../../lib/class-names"
|
|
3
|
+
|
|
4
|
+
type ViewControllerProps = PropsWithChildren<{
|
|
5
|
+
heading: ReactNode
|
|
6
|
+
className?: string
|
|
7
|
+
}>
|
|
8
|
+
|
|
9
|
+
type HeadingProps = {
|
|
10
|
+
title: ReactNode
|
|
11
|
+
description?: ReactNode
|
|
12
|
+
action?: ReactNode
|
|
13
|
+
size?: keyof typeof titleSizes
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const titleSizes = {
|
|
17
|
+
default: "text-3xl md:text-4xl",
|
|
18
|
+
// sign in style screens: centered, tighter, like a system sheet
|
|
19
|
+
compact: "text-[1.75rem] font-semibold tracking-tight",
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export function ViewController({
|
|
23
|
+
heading,
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
}: ViewControllerProps) {
|
|
27
|
+
return (
|
|
28
|
+
<article
|
|
29
|
+
className={cn(
|
|
30
|
+
"container mx-auto flex flex-col",
|
|
31
|
+
"w-full gap-5 py-20 md:max-w-3xl",
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
<section>{heading}</section>
|
|
36
|
+
{children}
|
|
37
|
+
</article>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function Heading({
|
|
42
|
+
title,
|
|
43
|
+
description,
|
|
44
|
+
action,
|
|
45
|
+
size = "default",
|
|
46
|
+
}: HeadingProps) {
|
|
47
|
+
const centered = size === "compact"
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<div
|
|
51
|
+
className={cn(
|
|
52
|
+
"flex flex-wrap items-center gap-3",
|
|
53
|
+
centered && "justify-center text-center"
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
<h1
|
|
57
|
+
className={cn(
|
|
58
|
+
"min-w-0 wrap-anywhere",
|
|
59
|
+
titleSizes[size]
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
{title}
|
|
63
|
+
</h1>
|
|
64
|
+
{action}
|
|
65
|
+
</div>
|
|
66
|
+
{description && (
|
|
67
|
+
<p
|
|
68
|
+
className={cn(
|
|
69
|
+
"mt-2 max-w-prose text-subheadline leading-relaxed text-label-secondary",
|
|
70
|
+
centered &&
|
|
71
|
+
"mt-1 text-center text-[15px]"
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
{description}
|
|
75
|
+
</p>
|
|
76
|
+
)}
|
|
77
|
+
</>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ViewController.Heading = Heading
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { buttonDocs } from "../components/button/button.docs"
|
|
2
|
+
import { iconButtonDocs } from "../components/button/icon-button.docs"
|
|
3
|
+
import { chipDocs } from "../components/chip/chip.docs"
|
|
4
|
+
import { labeledContentDocs } from "../components/labeled-content/labeled-content.docs"
|
|
5
|
+
import { pickerDocs } from "../components/picker/picker.docs"
|
|
6
|
+
import { searchFieldDocs } from "../components/search-field/search-field.docs"
|
|
7
|
+
import { secureFieldDocs } from "../components/secure-field/secure-field.docs"
|
|
8
|
+
import { sectionDocs } from "../components/section/section.docs"
|
|
9
|
+
import { segmentedControlDocs } from "../components/segmented-control/segmented-control.docs"
|
|
10
|
+
import { statTileDocs } from "../components/stat-tile/stat-tile.docs"
|
|
11
|
+
import { statusIndicatorDocs } from "../components/status-indicator/status-indicator.docs"
|
|
12
|
+
import { swatchPickerDocs } from "../components/swatch-picker/swatch-picker.docs"
|
|
13
|
+
import { textFieldDocs } from "../components/text-field/text-field.docs"
|
|
14
|
+
import { toggleDocs } from "../components/toggle/toggle.docs"
|
|
15
|
+
import { compactDatePickerDocs } from "../components/compact-date-picker/compact-date-picker.docs"
|
|
16
|
+
import { compactTimePickerDocs } from "../components/compact-time-picker/compact-time-picker.docs"
|
|
17
|
+
import { datePickerDocs } from "../components/date-picker/date-picker.docs"
|
|
18
|
+
import { textEditorDocs } from "../components/text-editor/text-editor.docs"
|
|
19
|
+
import { searchListDocs } from "../components/search-list/search-list.docs"
|
|
20
|
+
import { profileImageDocs } from "../components/profile-image/profile-image.docs"
|
|
21
|
+
import { tabViewDocs } from "../components/tab-view/tab-view.docs"
|
|
22
|
+
import { tabBarDocs } from "../components/tab-bar/tab-bar.docs"
|
|
23
|
+
import { sidebarDocs } from "../components/sidebar/sidebar.docs"
|
|
24
|
+
import { viewControllerDocs } from "../components/view-controller/view-controller.docs"
|
|
25
|
+
import { tableDocs } from "../components/table/table.docs"
|
|
26
|
+
import { contentViewDocs } from "../components/content-view/content-view.docs"
|
|
27
|
+
import { conversationDocs } from "../components/conversation/conversation.docs"
|
|
28
|
+
import { ambientDocs } from "../components/ambient/ambient.docs"
|
|
29
|
+
import { dialogDocs } from "../components/dialog/dialog.docs"
|
|
30
|
+
import { sheetDocs } from "../components/sheet/sheet.docs"
|
|
31
|
+
import { popoverDocs } from "../components/popover/popover.docs"
|
|
32
|
+
import { toastDocs } from "../components/toast/toast.docs"
|
|
33
|
+
import { contextMenuDocs } from "../components/context-menu/context-menu.docs"
|
|
34
|
+
import { tooltipDocs } from "../components/tooltip/tooltip.docs"
|
|
35
|
+
import { localeSwitcherDocs } from "../components/locale-switcher/locale-switcher.docs"
|
|
36
|
+
import type { RegisteredDocs } from "./types"
|
|
37
|
+
|
|
38
|
+
/** Every documented component. Add one line per docs file. */
|
|
39
|
+
export const registry: readonly RegisteredDocs[] = [
|
|
40
|
+
buttonDocs,
|
|
41
|
+
iconButtonDocs,
|
|
42
|
+
chipDocs,
|
|
43
|
+
textFieldDocs,
|
|
44
|
+
secureFieldDocs,
|
|
45
|
+
searchFieldDocs,
|
|
46
|
+
toggleDocs,
|
|
47
|
+
segmentedControlDocs,
|
|
48
|
+
pickerDocs,
|
|
49
|
+
swatchPickerDocs,
|
|
50
|
+
sectionDocs,
|
|
51
|
+
labeledContentDocs,
|
|
52
|
+
statusIndicatorDocs,
|
|
53
|
+
statTileDocs,
|
|
54
|
+
datePickerDocs,
|
|
55
|
+
compactDatePickerDocs,
|
|
56
|
+
compactTimePickerDocs,
|
|
57
|
+
textEditorDocs,
|
|
58
|
+
searchListDocs,
|
|
59
|
+
profileImageDocs,
|
|
60
|
+
tabViewDocs,
|
|
61
|
+
tabBarDocs,
|
|
62
|
+
sidebarDocs,
|
|
63
|
+
viewControllerDocs,
|
|
64
|
+
tableDocs,
|
|
65
|
+
contentViewDocs,
|
|
66
|
+
conversationDocs,
|
|
67
|
+
ambientDocs,
|
|
68
|
+
dialogDocs,
|
|
69
|
+
sheetDocs,
|
|
70
|
+
popoverDocs,
|
|
71
|
+
toastDocs,
|
|
72
|
+
contextMenuDocs,
|
|
73
|
+
tooltipDocs,
|
|
74
|
+
localeSwitcherDocs,
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
dialogDocs,
|
|
79
|
+
sheetDocs,
|
|
80
|
+
popoverDocs,
|
|
81
|
+
toastDocs,
|
|
82
|
+
contextMenuDocs,
|
|
83
|
+
tooltipDocs,
|
|
84
|
+
localeSwitcherDocs,
|
|
85
|
+
tableDocs,
|
|
86
|
+
contentViewDocs,
|
|
87
|
+
conversationDocs,
|
|
88
|
+
ambientDocs,
|
|
89
|
+
tabViewDocs,
|
|
90
|
+
tabBarDocs,
|
|
91
|
+
sidebarDocs,
|
|
92
|
+
viewControllerDocs,
|
|
93
|
+
datePickerDocs,
|
|
94
|
+
compactDatePickerDocs,
|
|
95
|
+
compactTimePickerDocs,
|
|
96
|
+
textEditorDocs,
|
|
97
|
+
searchListDocs,
|
|
98
|
+
profileImageDocs,
|
|
99
|
+
buttonDocs,
|
|
100
|
+
chipDocs,
|
|
101
|
+
iconButtonDocs,
|
|
102
|
+
labeledContentDocs,
|
|
103
|
+
pickerDocs,
|
|
104
|
+
searchFieldDocs,
|
|
105
|
+
secureFieldDocs,
|
|
106
|
+
sectionDocs,
|
|
107
|
+
segmentedControlDocs,
|
|
108
|
+
statTileDocs,
|
|
109
|
+
statusIndicatorDocs,
|
|
110
|
+
swatchPickerDocs,
|
|
111
|
+
textFieldDocs,
|
|
112
|
+
toggleDocs,
|
|
113
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A JSX attribute for a string, quoted so the code always parses: a
|
|
3
|
+
* plain `name="value"`, or `name={"value"}` when the value holds a quote,
|
|
4
|
+
* a brace, a backslash or a line break.
|
|
5
|
+
*/
|
|
6
|
+
export function jsxAttribute(name: string, value: string) {
|
|
7
|
+
return /["\\{}\n]/.test(value)
|
|
8
|
+
? `${name}={${JSON.stringify(value)}}`
|
|
9
|
+
: `${name}="${value}"`
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Text as JSX children, wrapped in braces when JSX would misread it. */
|
|
13
|
+
export function jsxText(text: string) {
|
|
14
|
+
return /[{}<>]/.test(text)
|
|
15
|
+
? `{${JSON.stringify(text)}}`
|
|
16
|
+
: text
|
|
17
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from "react"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A choice among fixed options. The options are object keys, so the
|
|
5
|
+
* record must name every member of the prop's union: adding a variant to
|
|
6
|
+
* a component fails the typecheck here until its docs list it too.
|
|
7
|
+
*/
|
|
8
|
+
export type SelectControl<T extends string> = {
|
|
9
|
+
kind: "select"
|
|
10
|
+
options: Record<T, true>
|
|
11
|
+
default: T
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type BooleanControl = {
|
|
15
|
+
kind: "boolean"
|
|
16
|
+
default: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type TextControl = {
|
|
20
|
+
kind: "text"
|
|
21
|
+
default: string
|
|
22
|
+
/**
|
|
23
|
+
* The prop is required, so the snippet always writes it, even at its
|
|
24
|
+
* default value, and the copied code compiles.
|
|
25
|
+
*/
|
|
26
|
+
required?: true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type AnyControl =
|
|
30
|
+
| SelectControl<string>
|
|
31
|
+
| BooleanControl
|
|
32
|
+
| TextControl
|
|
33
|
+
|
|
34
|
+
/** The control that fits a prop's type. */
|
|
35
|
+
export type ControlFor<V> = [NonNullable<V>] extends [boolean]
|
|
36
|
+
? BooleanControl
|
|
37
|
+
: [NonNullable<V>] extends [string]
|
|
38
|
+
? string extends NonNullable<V>
|
|
39
|
+
? TextControl
|
|
40
|
+
: SelectControl<NonNullable<V> & string>
|
|
41
|
+
: never
|
|
42
|
+
|
|
43
|
+
/** Controls for any of a component's props, plus its text children. */
|
|
44
|
+
export type Controls<P> = {
|
|
45
|
+
[K in Exclude<keyof P, "children">]?: ControlFor<P[K]>
|
|
46
|
+
} & { children?: TextControl }
|
|
47
|
+
|
|
48
|
+
/** The current value of every control. */
|
|
49
|
+
export type ControlValues<C> = {
|
|
50
|
+
[K in keyof C]-?: NonNullable<C[K]> extends {
|
|
51
|
+
kind: "select"
|
|
52
|
+
options: infer O
|
|
53
|
+
}
|
|
54
|
+
? keyof O & string
|
|
55
|
+
: NonNullable<C[K]> extends BooleanControl
|
|
56
|
+
? boolean
|
|
57
|
+
: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* An example is a real React function in the component's `examples/`
|
|
62
|
+
* folder, named `<slug>-<id>.tsx`; the site shows that file's source as
|
|
63
|
+
* its code, so the code is always what runs. `playground` values let the
|
|
64
|
+
* site open the example in the playground.
|
|
65
|
+
*/
|
|
66
|
+
export type Example<C> = {
|
|
67
|
+
id: string
|
|
68
|
+
component: ComponentType
|
|
69
|
+
playground?: Partial<ControlValues<C>>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** What the code snippet shows for children, when plain text is not enough. */
|
|
73
|
+
export type SnippetChildren = {
|
|
74
|
+
imports: string[]
|
|
75
|
+
children: string
|
|
76
|
+
/** Literal string props, written as name="value". */
|
|
77
|
+
extraProps?: Record<string, string>
|
|
78
|
+
/** Code expressions, written as name={expression}: state and handlers. */
|
|
79
|
+
expressionProps?: Record<string, string>
|
|
80
|
+
/** Objects spread onto the element, written as {...name}. */
|
|
81
|
+
spreadProps?: string[]
|
|
82
|
+
/** Lines placed before the element, such as a useState call. */
|
|
83
|
+
setup?: string[]
|
|
84
|
+
/**
|
|
85
|
+
* Demo-only controls the snippet writes itself, left out of the
|
|
86
|
+
* generated props: a `critical` switch written as `tone="critical"`.
|
|
87
|
+
*/
|
|
88
|
+
omit?: string[]
|
|
89
|
+
/**
|
|
90
|
+
* The element to write when it is a part rather than the export
|
|
91
|
+
* itself: `Conversation.Messages` for `Conversation`.
|
|
92
|
+
*/
|
|
93
|
+
element?: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type ComponentDocs<
|
|
97
|
+
C,
|
|
98
|
+
E extends readonly Example<C>[],
|
|
99
|
+
> = {
|
|
100
|
+
/** Kebab case, unique across the registry, used in URLs. */
|
|
101
|
+
slug: string
|
|
102
|
+
/** The component's export name, as written in code. */
|
|
103
|
+
name: string
|
|
104
|
+
/** Where it is imported from, for example `@passui/react/button`. */
|
|
105
|
+
importPath: string
|
|
106
|
+
controls: C
|
|
107
|
+
render: (values: ControlValues<C>) => ReactNode
|
|
108
|
+
childrenSnippet?: (
|
|
109
|
+
values: ControlValues<C>
|
|
110
|
+
) => SnippetChildren
|
|
111
|
+
/** The smallest real use, shown under Usage (`<slug>-usage.tsx`). */
|
|
112
|
+
usage: ComponentType
|
|
113
|
+
/** A small, realistic interface built with it (`<slug>-showcase.tsx`). */
|
|
114
|
+
showcase: ComponentType
|
|
115
|
+
examples: E
|
|
116
|
+
/**
|
|
117
|
+
* The playground frame's least height in pixels, for components that
|
|
118
|
+
* open something over the page (a sheet, a popover, a menu) that the
|
|
119
|
+
* frame must have room to show.
|
|
120
|
+
*/
|
|
121
|
+
previewHeight?: number
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Rejects select options that are not members of the prop's union, so
|
|
126
|
+
* docs cannot offer a value the component does not accept.
|
|
127
|
+
*/
|
|
128
|
+
type ExactOptions<C, P> = {
|
|
129
|
+
[K in keyof C]: K extends keyof P
|
|
130
|
+
? C[K] extends { kind: "select"; options: infer O }
|
|
131
|
+
? {
|
|
132
|
+
options: {
|
|
133
|
+
[Option in keyof O]: Option extends NonNullable<
|
|
134
|
+
P[K]
|
|
135
|
+
>
|
|
136
|
+
? true
|
|
137
|
+
: never
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
: unknown
|
|
141
|
+
: unknown
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Docs typed against a component's props. Curried so the props type is
|
|
146
|
+
* given once and control keys and example ids stay exact:
|
|
147
|
+
* `defineDocs<ButtonProps>()({ ... })`.
|
|
148
|
+
*/
|
|
149
|
+
export function defineDocs<P>() {
|
|
150
|
+
return <
|
|
151
|
+
const C extends Controls<P>,
|
|
152
|
+
const E extends readonly Example<C>[],
|
|
153
|
+
>(
|
|
154
|
+
docs: ComponentDocs<C, E> & {
|
|
155
|
+
controls: ExactOptions<C, P>
|
|
156
|
+
}
|
|
157
|
+
) => docs
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Any component's docs, as the registry holds them. */
|
|
161
|
+
export type RegisteredDocs = {
|
|
162
|
+
slug: string
|
|
163
|
+
name: string
|
|
164
|
+
importPath: string
|
|
165
|
+
controls: Record<string, AnyControl | undefined>
|
|
166
|
+
examples: readonly {
|
|
167
|
+
id: string
|
|
168
|
+
component: ComponentType
|
|
169
|
+
playground?: Record<string, string | boolean>
|
|
170
|
+
}[]
|
|
171
|
+
usage: ComponentType
|
|
172
|
+
showcase: ComponentType
|
|
173
|
+
previewHeight?: number
|
|
174
|
+
render(
|
|
175
|
+
values: Record<string, string | boolean>
|
|
176
|
+
): ReactNode
|
|
177
|
+
childrenSnippet?(
|
|
178
|
+
values: Record<string, string | boolean>
|
|
179
|
+
): SnippetChildren
|
|
180
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Programmatic restoration does not collapse chrome; reaching the top always restores it. */
|
|
2
|
+
export function minimizeDecision(
|
|
3
|
+
top: number,
|
|
4
|
+
anchor: number,
|
|
5
|
+
userDriven: boolean
|
|
6
|
+
): boolean | null {
|
|
7
|
+
if (top < 24) return false
|
|
8
|
+
if (!userDriven) return null
|
|
9
|
+
const delta = top - anchor
|
|
10
|
+
if (delta < -12) return false
|
|
11
|
+
if (delta > 12) return true
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useLayoutEffect, useState } from "react"
|
|
2
|
+
import {
|
|
3
|
+
type AnchoredPlacement,
|
|
4
|
+
placeAnchored,
|
|
5
|
+
} from "../lib/anchored-placement"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Places a panel of `width` and `needed` height under its anchor and
|
|
9
|
+
* keeps it there: re-places while the viewport resizes or anything
|
|
10
|
+
* scrolls, so it never hangs off the screen.
|
|
11
|
+
*/
|
|
12
|
+
export function useAnchoredPlacement(
|
|
13
|
+
anchor: HTMLElement,
|
|
14
|
+
width: number,
|
|
15
|
+
needed: number
|
|
16
|
+
): AnchoredPlacement {
|
|
17
|
+
const [placement, setPlacement] = useState(() =>
|
|
18
|
+
placeAnchored(anchor, width, needed)
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
const replace = () =>
|
|
23
|
+
setPlacement(placeAnchored(anchor, width, needed))
|
|
24
|
+
replace()
|
|
25
|
+
const viewport = window.visualViewport
|
|
26
|
+
viewport?.addEventListener("resize", replace)
|
|
27
|
+
viewport?.addEventListener("scroll", replace)
|
|
28
|
+
window.addEventListener("resize", replace)
|
|
29
|
+
window.addEventListener("scroll", replace, true)
|
|
30
|
+
return () => {
|
|
31
|
+
viewport?.removeEventListener("resize", replace)
|
|
32
|
+
viewport?.removeEventListener("scroll", replace)
|
|
33
|
+
window.removeEventListener("resize", replace)
|
|
34
|
+
window.removeEventListener(
|
|
35
|
+
"scroll",
|
|
36
|
+
replace,
|
|
37
|
+
true
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
}, [anchor, width, needed])
|
|
41
|
+
|
|
42
|
+
return placement
|
|
43
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type RefObject, useEffect } from "react"
|
|
2
|
+
import { ownsFloatingLayer } from "../lib/floating-layers"
|
|
3
|
+
|
|
4
|
+
/** Marks a floating layer (a pop-up list, a menu) whose clicks belong to what opened it. */
|
|
5
|
+
export { FLOATING_LAYER } from "../lib/floating-layers"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Calls `onDismiss` when the user presses Escape or clicks outside `ref`
|
|
9
|
+
* while `active`, for menus, popovers and sheets. Clicks in a floating
|
|
10
|
+
* layer opened from inside, such as a pop-up button's list, don't count.
|
|
11
|
+
*/
|
|
12
|
+
export function useDismiss(
|
|
13
|
+
ref: RefObject<HTMLElement | null>,
|
|
14
|
+
active: boolean,
|
|
15
|
+
onDismiss: () => void
|
|
16
|
+
): void {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!active) return
|
|
19
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
20
|
+
const target = event.target as Element
|
|
21
|
+
const surface = ref.current
|
|
22
|
+
if (
|
|
23
|
+
surface &&
|
|
24
|
+
(surface.contains(target) ||
|
|
25
|
+
ownsFloatingLayer(surface, target))
|
|
26
|
+
)
|
|
27
|
+
return
|
|
28
|
+
onDismiss()
|
|
29
|
+
}
|
|
30
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
31
|
+
if (event.defaultPrevented) return
|
|
32
|
+
if (
|
|
33
|
+
ref.current &&
|
|
34
|
+
ownsFloatingLayer(
|
|
35
|
+
ref.current,
|
|
36
|
+
document.activeElement
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
if (event.key === "Escape") {
|
|
41
|
+
event.preventDefault()
|
|
42
|
+
onDismiss()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
document.addEventListener(
|
|
46
|
+
"pointerdown",
|
|
47
|
+
onPointerDown
|
|
48
|
+
)
|
|
49
|
+
window.addEventListener("keydown", onKeyDown)
|
|
50
|
+
return () => {
|
|
51
|
+
document.removeEventListener(
|
|
52
|
+
"pointerdown",
|
|
53
|
+
onPointerDown
|
|
54
|
+
)
|
|
55
|
+
window.removeEventListener("keydown", onKeyDown)
|
|
56
|
+
}
|
|
57
|
+
}, [ref, active, onDismiss])
|
|
58
|
+
}
|