@koide-labs/ui 0.0.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.
Files changed (170) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/.storybook/main.ts +25 -0
  3. package/.storybook/preview-head.html +6 -0
  4. package/.storybook/preview.tsx +48 -0
  5. package/.storybook/vitest.setup.ts +8 -0
  6. package/README.md +11 -0
  7. package/eslint.config.mjs +29 -0
  8. package/lint-staged.config.js +15 -0
  9. package/package.json +95 -0
  10. package/pnpm-workspace.yaml +2 -0
  11. package/postcss.config.mjs +7 -0
  12. package/prettier.config.mjs +24 -0
  13. package/scripts/build-icon-types.ts +38 -0
  14. package/src/-types.ts +8 -0
  15. package/src/-utils.tsx +64 -0
  16. package/src/components/accordion/accordion.module.css +44 -0
  17. package/src/components/accordion/accordion.stories.tsx +36 -0
  18. package/src/components/accordion/index.tsx +67 -0
  19. package/src/components/alert-dialog/alert-dialog.module.css +5 -0
  20. package/src/components/alert-dialog/alert-dialog.stories.tsx +53 -0
  21. package/src/components/alert-dialog/index.tsx +138 -0
  22. package/src/components/anchor/anchor.module.css +18 -0
  23. package/src/components/anchor/anchor.stories.tsx +28 -0
  24. package/src/components/anchor/index.tsx +45 -0
  25. package/src/components/avatar/avatar.module.css +56 -0
  26. package/src/components/avatar/avatar.stories.tsx +61 -0
  27. package/src/components/avatar/index.tsx +82 -0
  28. package/src/components/badge/badge.module.css +35 -0
  29. package/src/components/badge/badge.stories.tsx +60 -0
  30. package/src/components/badge/index.tsx +71 -0
  31. package/src/components/button/button.module.css +42 -0
  32. package/src/components/button/button.stories.tsx +108 -0
  33. package/src/components/button/index.tsx +63 -0
  34. package/src/components/checkbox/checkbox.module.css +36 -0
  35. package/src/components/checkbox/checkbox.stories.tsx +21 -0
  36. package/src/components/checkbox/index.tsx +41 -0
  37. package/src/components/code/code.module.css +20 -0
  38. package/src/components/code/code.stories.tsx +42 -0
  39. package/src/components/code/index.tsx +73 -0
  40. package/src/components/collapse/collapse.module.css +27 -0
  41. package/src/components/collapse/collapse.stories.tsx +27 -0
  42. package/src/components/collapse/index.tsx +59 -0
  43. package/src/components/command/command.module.css +95 -0
  44. package/src/components/command/command.stories.tsx +38 -0
  45. package/src/components/command/index.tsx +108 -0
  46. package/src/components/context-menu/context-menu.module.css +36 -0
  47. package/src/components/context-menu/context-menu.stories.tsx +99 -0
  48. package/src/components/context-menu/index.tsx +242 -0
  49. package/src/components/dialog/dialog.module.css +71 -0
  50. package/src/components/dialog/dialog.stories.tsx +29 -0
  51. package/src/components/dialog/index.tsx +148 -0
  52. package/src/components/heading/heading.module.css +3 -0
  53. package/src/components/heading/heading.stories.tsx +52 -0
  54. package/src/components/heading/index.tsx +112 -0
  55. package/src/components/icon/icon-names.ts +3189 -0
  56. package/src/components/icon/icon.module.css +36 -0
  57. package/src/components/icon/icon.stories.tsx +40 -0
  58. package/src/components/icon/index.tsx +60 -0
  59. package/src/components/icon-button/icon-button.module.css +33 -0
  60. package/src/components/icon-button/icon-button.stories.tsx +59 -0
  61. package/src/components/icon-button/index.tsx +48 -0
  62. package/src/components/inline-code/index.tsx +29 -0
  63. package/src/components/inline-code/inline-code.module.css +13 -0
  64. package/src/components/inline-code/inline-code.stories.tsx +31 -0
  65. package/src/components/input/index.tsx +22 -0
  66. package/src/components/input/input.module.css +23 -0
  67. package/src/components/input/input.stories.tsx +52 -0
  68. package/src/components/meter/index.tsx +55 -0
  69. package/src/components/meter/meter.module.css +23 -0
  70. package/src/components/meter/meter.stories.tsx +31 -0
  71. package/src/components/multiline-input/index.tsx +58 -0
  72. package/src/components/multiline-input/multiline-input.stories.tsx +26 -0
  73. package/src/components/number-input/index.tsx +74 -0
  74. package/src/components/number-input/number-input.module.css +41 -0
  75. package/src/components/number-input/number-input.stories.tsx +24 -0
  76. package/src/components/password-input/index.tsx +24 -0
  77. package/src/components/password-input/password-input.module.css +10 -0
  78. package/src/components/password-input/password-input.stories.tsx +24 -0
  79. package/src/components/pill/index.tsx +45 -0
  80. package/src/components/pill/pill.module.css +22 -0
  81. package/src/components/pill/pill.stories.tsx +83 -0
  82. package/src/components/popover/index.tsx +94 -0
  83. package/src/components/popover/popover.module.css +8 -0
  84. package/src/components/popover/popover.stories.tsx +53 -0
  85. package/src/components/preview-card/index.tsx +68 -0
  86. package/src/components/preview-card/preview-card.module.css +5 -0
  87. package/src/components/preview-card/preview-card.stories.tsx +58 -0
  88. package/src/components/radio/index.tsx +67 -0
  89. package/src/components/radio/radio-group.module.css +5 -0
  90. package/src/components/radio/radio.module.css +36 -0
  91. package/src/components/radio/radio.stories.tsx +27 -0
  92. package/src/components/search-bar/index.tsx +60 -0
  93. package/src/components/search-bar/search-bar.module.css +29 -0
  94. package/src/components/search-bar/search-bar.stories.tsx +37 -0
  95. package/src/components/select/index.tsx +132 -0
  96. package/src/components/select/select.module.css +63 -0
  97. package/src/components/select/select.stories.tsx +49 -0
  98. package/src/components/separator/index.tsx +28 -0
  99. package/src/components/separator/separator.module.css +24 -0
  100. package/src/components/separator/separator.stories.tsx +40 -0
  101. package/src/components/slider/index.tsx +28 -0
  102. package/src/components/slider/slider.module.css +52 -0
  103. package/src/components/slider/slider.stories.tsx +53 -0
  104. package/src/components/spinner/index.tsx +14 -0
  105. package/src/components/spinner/spinner.module.css +13 -0
  106. package/src/components/spinner/spinner.stories.tsx +17 -0
  107. package/src/components/stacked-avatars/index.tsx +88 -0
  108. package/src/components/stacked-avatars/stacked-avatars.module.css +79 -0
  109. package/src/components/stacked-avatars/stacked-avatars.stories.tsx +48 -0
  110. package/src/components/status-banner/index.tsx +96 -0
  111. package/src/components/status-banner/status-banner.module.css +52 -0
  112. package/src/components/status-banner/status-banner.stories.tsx +44 -0
  113. package/src/components/surface/index.tsx +83 -0
  114. package/src/components/surface/surface.module.css +35 -0
  115. package/src/components/surface/surface.stories.tsx +84 -0
  116. package/src/components/switch/index.tsx +23 -0
  117. package/src/components/switch/switch.module.css +45 -0
  118. package/src/components/switch/switch.stories.tsx +48 -0
  119. package/src/components/tabs/index.tsx +126 -0
  120. package/src/components/tabs/tabs.module.css +134 -0
  121. package/src/components/tabs/tabs.stories.tsx +88 -0
  122. package/src/components/text/index.tsx +69 -0
  123. package/src/components/text/text.module.css +76 -0
  124. package/src/components/text/text.stories.tsx +107 -0
  125. package/src/components/theme-provider/index.ts +2 -0
  126. package/src/components/theme-provider/theme-context.tsx +18 -0
  127. package/src/components/theme-provider/theme-provider.stories.tsx +47 -0
  128. package/src/components/theme-provider/theme-provider.tsx +77 -0
  129. package/src/components/timestamp/index.tsx +131 -0
  130. package/src/components/timestamp/timestamp.module.css +8 -0
  131. package/src/components/timestamp/timestamp.stories.tsx +37 -0
  132. package/src/components/toast/index.ts +2 -0
  133. package/src/components/toast/toast.module.css +163 -0
  134. package/src/components/toast/toast.stories.tsx +53 -0
  135. package/src/components/toast/toast.tsx +104 -0
  136. package/src/components/toast/use-toast-manager.ts +63 -0
  137. package/src/components/tooltip/index.tsx +61 -0
  138. package/src/components/tooltip/tooltip-arrow.tsx +17 -0
  139. package/src/components/tooltip/tooltip.module.css +44 -0
  140. package/src/components/tooltip/tooltip.stories.tsx +76 -0
  141. package/src/components/view/index.tsx +137 -0
  142. package/src/components/view/view.module.css +11 -0
  143. package/src/components/view/view.stories.tsx +131 -0
  144. package/src/components/view/view_colorway.module.css +280 -0
  145. package/src/components/view/view_interactive.module.css +127 -0
  146. package/src/components/view/view_loading.module.css +58 -0
  147. package/src/components/visually-hidden/index.ts +1 -0
  148. package/src/index.ts +49 -0
  149. package/src/integrations/react-markdown/index.tsx +134 -0
  150. package/src/integrations/react-markdown/react-markdown.module.css +62 -0
  151. package/src/integrations/react-markdown/react-markdown.stories.tsx +31 -0
  152. package/src/integrations/remix.ts +12 -0
  153. package/src/integrations/tailwind.css +173 -0
  154. package/src/integrations/twemoij/index.tsx +13 -0
  155. package/src/integrations/twemoij/twemoji.module.css +7 -0
  156. package/src/integrations/twemoij/twemoji.stories.tsx +40 -0
  157. package/src/stories/components/all-variants.tsx +40 -0
  158. package/src/stories/data.ts +72 -0
  159. package/src/stories/utils.ts +20 -0
  160. package/src/styles/core.css +153 -0
  161. package/src/styles/themes/dark.css +86 -0
  162. package/src/styles/themes/light.css +86 -0
  163. package/src/styles/tokens.ts +282 -0
  164. package/src/styles/transitions.module.css +31 -0
  165. package/stylelint.config.mjs +29 -0
  166. package/tsconfig.app.json +35 -0
  167. package/tsconfig.json +7 -0
  168. package/tsconfig.node.json +26 -0
  169. package/vite.config.ts +103 -0
  170. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,108 @@
1
+ import clsx from "clsx";
2
+ import { Command as CommandPrimitive } from "cmdk";
3
+ import type { ComponentProps } from "react";
4
+
5
+ import { textify } from "~/-utils";
6
+
7
+ import { Dialog, type BaseDialogProps } from "../dialog";
8
+ import { Icon } from "../icon";
9
+ import type { IconName } from "../icon/icon-names";
10
+ import { Separator } from "../separator";
11
+ import { Text } from "../text";
12
+ import { View } from "../view";
13
+
14
+ import styles from "./command.module.css";
15
+
16
+ // TODO put background in dialog base props
17
+ export type CommandDialogProps = Omit<
18
+ BaseDialogProps,
19
+ "title" | "description" | "background"
20
+ > & {
21
+ placeholder?: string;
22
+ };
23
+
24
+ export function CommandDialog({
25
+ placeholder,
26
+ children,
27
+ ...props
28
+ }: CommandDialogProps) {
29
+ return (
30
+ <Dialog
31
+ width="sm"
32
+ background="default"
33
+ className={styles["command"]}
34
+ {...props}
35
+ closable
36
+ >
37
+ <CommandPrimitive>
38
+ <View className={styles["command__input-root"]}>
39
+ <View
40
+ interactive="fill-outline"
41
+ className={styles["command__input"]}
42
+ render={<CommandPrimitive.Input placeholder={placeholder} />}
43
+ />
44
+ <Icon name="search-line" className={styles["command__input-icon"]} />
45
+ </View>
46
+
47
+ <CommandPrimitive.List className={styles["command__list"]}>
48
+ <CommandPrimitive.Empty>
49
+ <View className={styles["command__empty"]}>
50
+ <Text color="dimmer">No results found.</Text>
51
+ </View>
52
+ </CommandPrimitive.Empty>
53
+
54
+ {children}
55
+ </CommandPrimitive.List>
56
+ </CommandPrimitive>
57
+ </Dialog>
58
+ );
59
+ }
60
+
61
+ export function CommandGroup({
62
+ className,
63
+ ...props
64
+ }: ComponentProps<typeof CommandPrimitive.Group>) {
65
+ return (
66
+ <CommandPrimitive.Group
67
+ className={clsx(styles["command__group"], className)}
68
+ {...props}
69
+ />
70
+ );
71
+ }
72
+
73
+ export function CommandSeparator(
74
+ props: ComponentProps<typeof CommandPrimitive.Separator>,
75
+ ) {
76
+ return <Separator render={<CommandPrimitive.Separator {...props} />} />;
77
+ }
78
+
79
+ export function CommandItem({
80
+ shortcut,
81
+ icon,
82
+ className,
83
+ children,
84
+ ...props
85
+ }: ComponentProps<typeof CommandPrimitive.Item> & {
86
+ shortcut?: string;
87
+ icon?: IconName;
88
+ }) {
89
+ return (
90
+ <View
91
+ interactive="list-item"
92
+ className={clsx(styles["command__item"], className)}
93
+ render={<CommandPrimitive.Item {...props} />}
94
+ >
95
+ {icon ? <Icon name={icon} /> : undefined}
96
+ {textify(children)}
97
+ {shortcut ? (
98
+ <Text
99
+ className={clsx(styles["command__shortcut"])}
100
+ color="dimmer"
101
+ size="sm"
102
+ >
103
+ {shortcut}
104
+ </Text>
105
+ ) : null}
106
+ </View>
107
+ );
108
+ }
@@ -0,0 +1,36 @@
1
+ .context-menu {
2
+ gap: var(--space-4);
3
+ box-shadow: var(--shadow-1);
4
+ border: 1px solid var(--outline-dimmest);
5
+ border-radius: var(--border-radius-default);
6
+ padding: var(--space-4);
7
+ user-select: none;
8
+ }
9
+
10
+ .context-menu__item {
11
+ position: relative;
12
+ flex-direction: row;
13
+ align-items: center;
14
+ gap: var(--space-4);
15
+ outline: none;
16
+ border: none;
17
+ border-radius: var(--border-radius-6);
18
+ padding: var(--space-2) var(--space-4);
19
+ padding-left: var(--space-24);
20
+ }
21
+
22
+ .context-menu__item-icon {
23
+ position: absolute;
24
+ top: 50%;
25
+ left: var(--space-4);
26
+ transform: translateY(-50%);
27
+ }
28
+
29
+ .context-menu__item-shortcut {
30
+ margin-left: auto;
31
+ letter-spacing: 0.1em;
32
+ }
33
+
34
+ .context-menu__label {
35
+ padding: var(--space-2) var(--space-4);
36
+ }
@@ -0,0 +1,99 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { tokens } from "~/styles/tokens";
4
+
5
+ import {
6
+ ContextMenu,
7
+ ContextMenuCheckboxItem,
8
+ ContextMenuGroup,
9
+ ContextMenuItem,
10
+ ContextMenuMore,
11
+ ContextMenuRadioGroup,
12
+ ContextMenuRadioItem,
13
+ ContextMenuSeparator,
14
+ } from ".";
15
+ import { Text } from "../text";
16
+ import { View } from "../view";
17
+
18
+ const meta = {
19
+ title: "ContextMenu",
20
+ component: ContextMenu,
21
+ parameters: { layout: "centered" },
22
+ } satisfies Meta<typeof ContextMenu>;
23
+
24
+ export default meta;
25
+
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ export const Default: Story = {
29
+ render: () => (
30
+ <>
31
+ <ContextMenu
32
+ style={{ width: "200px" }}
33
+ trigger={
34
+ <View
35
+ style={{
36
+ height: tokens.space128,
37
+ width: tokens.space256,
38
+ border: `1px dashed ${tokens.primaryDefault}`,
39
+ backgroundColor: tokens.primaryDimmest,
40
+ borderRadius: tokens.borderRadiusDefault,
41
+ color: tokens.primaryDefault,
42
+ alignItems: "center",
43
+ justifyContent: "center",
44
+ }}
45
+ >
46
+ <Text color="inherit">Right click here</Text>
47
+ </View>
48
+ }
49
+ >
50
+ <ContextMenuGroup>
51
+ <ContextMenuItem icon="arrow-left-line" disabled shortcut="⌘+[">
52
+ Back
53
+ </ContextMenuItem>
54
+ <ContextMenuItem icon="arrow-right-line" shortcut="⌘+]">
55
+ Forward
56
+ </ContextMenuItem>
57
+ <ContextMenuItem icon="refresh-line">Reload</ContextMenuItem>
58
+ <ContextMenuMore icon="tools-line" label="More Tools">
59
+ <ContextMenuGroup>
60
+ <ContextMenuItem icon="download-line">
61
+ Save Page As
62
+ </ContextMenuItem>
63
+ <ContextMenuItem icon="links-line">
64
+ Create Shortcut
65
+ </ContextMenuItem>
66
+ <ContextMenuItem icon="window-2-line">
67
+ Name Window
68
+ </ContextMenuItem>
69
+ </ContextMenuGroup>
70
+ <ContextMenuSeparator />
71
+ <ContextMenuItem icon="terminal-box-line">
72
+ Developer Tools
73
+ </ContextMenuItem>
74
+ </ContextMenuMore>
75
+ </ContextMenuGroup>
76
+
77
+ <ContextMenuSeparator />
78
+
79
+ <ContextMenuGroup>
80
+ <ContextMenuCheckboxItem value="bookmarks">
81
+ Show Bookmarks
82
+ </ContextMenuCheckboxItem>
83
+ <ContextMenuCheckboxItem value="show_full_urls">
84
+ Show Full URLs
85
+ </ContextMenuCheckboxItem>
86
+ </ContextMenuGroup>
87
+
88
+ <ContextMenuSeparator />
89
+
90
+ <ContextMenuGroup label="People">
91
+ <ContextMenuRadioGroup defaultValue="a">
92
+ <ContextMenuRadioItem value="a">Pedro Duarte</ContextMenuRadioItem>
93
+ <ContextMenuRadioItem value="b">Colm Tuite</ContextMenuRadioItem>
94
+ </ContextMenuRadioGroup>
95
+ </ContextMenuGroup>
96
+ </ContextMenu>
97
+ </>
98
+ ),
99
+ };
@@ -0,0 +1,242 @@
1
+ import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu";
2
+ import clsx from "clsx";
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ type CSSProperties,
7
+ type ReactElement,
8
+ type ReactNode,
9
+ } from "react";
10
+
11
+ import { textify } from "~/-utils";
12
+ import { tokens } from "~/styles/tokens";
13
+
14
+ import { Icon } from "../icon";
15
+ import type { IconName } from "../icon/icon-names";
16
+ import { Separator, type SeparatorProps } from "../separator";
17
+ import { Surface, type Background } from "../surface";
18
+ import { Text } from "../text";
19
+ import { View } from "../view";
20
+
21
+ import transitionStyles from "../../styles/transitions.module.css";
22
+ import styles from "./context-menu.module.css";
23
+
24
+ export type ContextMenuProps = ContextMenuPrimitive.Root.Props & {
25
+ /** Specify trigger to open context menu. */
26
+ trigger?: ReactElement;
27
+
28
+ /** Surface background popup should use. Defaults to "root". Propogates to submenus. */
29
+ background?: Background;
30
+
31
+ /** Context menu content */
32
+ children?: ReactNode;
33
+
34
+ /** Apply className to ContextMenuPrimitive.Popup */
35
+ className?: string;
36
+
37
+ /** Apply styles to ContextMenuPrimitive.Popup */
38
+ style?: CSSProperties;
39
+ };
40
+
41
+ const ContextMenuContext = createContext<Background>("root");
42
+
43
+ export function ContextMenu({
44
+ trigger,
45
+ background = "root",
46
+ className,
47
+ children,
48
+ style,
49
+ ...props
50
+ }: ContextMenuProps) {
51
+ return (
52
+ <ContextMenuContext.Provider value={background}>
53
+ <ContextMenuPrimitive.Root {...props}>
54
+ {trigger ? <ContextMenuPrimitive.Trigger render={trigger} /> : null}
55
+ <ContextMenuPrimitive.Portal>
56
+ <ContextMenuPrimitive.Positioner>
57
+ <ContextMenuPrimitive.Popup
58
+ render={<Surface background={background} />}
59
+ className={clsx(
60
+ styles["context-menu"],
61
+ transitionStyles["transition_fade-out"],
62
+ className,
63
+ )}
64
+ style={style}
65
+ >
66
+ {children}
67
+ </ContextMenuPrimitive.Popup>
68
+ </ContextMenuPrimitive.Positioner>
69
+ </ContextMenuPrimitive.Portal>
70
+ </ContextMenuPrimitive.Root>
71
+ </ContextMenuContext.Provider>
72
+ );
73
+ }
74
+
75
+ export function ContextMenuItem({
76
+ icon,
77
+ shortcut,
78
+ className,
79
+ children,
80
+ ...props
81
+ }: ContextMenuPrimitive.Item.Props & {
82
+ /** Optional item icon to communicate purpose */
83
+ icon?: IconName;
84
+
85
+ /** Indicate that this action can be triggered by a keyboard shortcut */
86
+ shortcut?: string;
87
+ }) {
88
+ return (
89
+ <View
90
+ render={<ContextMenuPrimitive.Item {...props} />}
91
+ interactive="list-item"
92
+ className={clsx(styles["context-menu__item"], className)}
93
+ >
94
+ {icon ? (
95
+ <Icon name={icon} className={styles["context-menu__item-icon"]} />
96
+ ) : null}
97
+ {textify(children)}
98
+ {shortcut ? (
99
+ <Text
100
+ className={styles["context-menu__item-shortcut"]}
101
+ color="dimmest"
102
+ size="sm"
103
+ >
104
+ {shortcut}
105
+ </Text>
106
+ ) : null}
107
+ </View>
108
+ );
109
+ }
110
+
111
+ export function ContextMenuSeparator(
112
+ props: ContextMenuPrimitive.Separator.Props & SeparatorProps,
113
+ ) {
114
+ return <Separator render={<ContextMenuPrimitive.Separator />} {...props} />;
115
+ }
116
+
117
+ export function ContextMenuGroup({
118
+ label,
119
+ children,
120
+ ...props
121
+ }: ContextMenuPrimitive.Group.Props & {
122
+ label?: string;
123
+ }) {
124
+ return (
125
+ <ContextMenuPrimitive.Group {...props}>
126
+ {label ? (
127
+ <Text
128
+ render={<ContextMenuPrimitive.GroupLabel />}
129
+ className={styles["context-menu__label"]}
130
+ color="dimmest"
131
+ size="sm"
132
+ >
133
+ {label}
134
+ </Text>
135
+ ) : null}
136
+ {children}
137
+ </ContextMenuPrimitive.Group>
138
+ );
139
+ }
140
+
141
+ export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
142
+
143
+ export function ContextMenuRadioItem({
144
+ className,
145
+ children,
146
+ ...props
147
+ }: ContextMenuPrimitive.RadioItem.Props) {
148
+ return (
149
+ <View
150
+ interactive="list-item"
151
+ render={<ContextMenuPrimitive.RadioItem {...props} />}
152
+ className={clsx(styles["context-menu__item"], className)}
153
+ >
154
+ <ContextMenuPrimitive.RadioItemIndicator
155
+ className={styles["context-menu__item-icon"]}
156
+ >
157
+ <Icon name="checkbox-circle-line" color={tokens.primaryDefault} />
158
+ </ContextMenuPrimitive.RadioItemIndicator>
159
+ {textify(children)}
160
+ </View>
161
+ );
162
+ }
163
+
164
+ export function ContextMenuCheckboxItem({
165
+ className,
166
+ children,
167
+ ...props
168
+ }: ContextMenuPrimitive.RadioItem.Props) {
169
+ return (
170
+ <View
171
+ interactive="list-item"
172
+ render={<ContextMenuPrimitive.CheckboxItem {...props} />}
173
+ className={clsx(styles["context-menu__item"], className)}
174
+ >
175
+ <ContextMenuPrimitive.CheckboxItemIndicator
176
+ className={styles["context-menu__item-icon"]}
177
+ >
178
+ <Icon name="check-line" color={tokens.primaryDefault} />
179
+ </ContextMenuPrimitive.CheckboxItemIndicator>
180
+ {textify(children)}
181
+ </View>
182
+ );
183
+ }
184
+
185
+ export function ContextMenuMore({
186
+ label,
187
+ icon,
188
+ children,
189
+ className,
190
+ style,
191
+ ...props
192
+ }: ContextMenuPrimitive.SubmenuRoot.Props & {
193
+ label: string;
194
+
195
+ /** Optional item icon to communicate purpose */
196
+ icon?: IconName;
197
+
198
+ /** Context menu content */
199
+ children?: ReactNode;
200
+
201
+ /** Apply className to ContextMenuPrimitive.Popup */
202
+ className?: string;
203
+
204
+ /** Apply styles to ContextMenuPrimitive.Popup */
205
+ style?: CSSProperties;
206
+ }) {
207
+ const background = useContext(ContextMenuContext);
208
+
209
+ return (
210
+ <ContextMenuPrimitive.SubmenuRoot {...props}>
211
+ <View
212
+ render={<ContextMenuPrimitive.SubmenuTrigger />}
213
+ className={styles["context-menu__item"]}
214
+ >
215
+ {icon ? (
216
+ <Icon name={icon} className={styles["context-menu__item-icon"]} />
217
+ ) : null}
218
+ {textify(label)}
219
+ <Icon
220
+ name="arrow-right-line"
221
+ className={styles["context-menu__item-shortcut"]}
222
+ />
223
+ </View>
224
+
225
+ <ContextMenuPrimitive.Portal>
226
+ <ContextMenuPrimitive.Positioner alignOffset={-4} sideOffset={-4}>
227
+ <ContextMenuPrimitive.Popup
228
+ render={<Surface background={background} />}
229
+ className={clsx(
230
+ styles["context-menu"],
231
+ transitionStyles["transition_scale"],
232
+ className,
233
+ )}
234
+ style={style}
235
+ >
236
+ {children}
237
+ </ContextMenuPrimitive.Popup>
238
+ </ContextMenuPrimitive.Positioner>
239
+ </ContextMenuPrimitive.Portal>
240
+ </ContextMenuPrimitive.SubmenuRoot>
241
+ );
242
+ }
@@ -0,0 +1,71 @@
1
+ .dialog__backdrop {
2
+ position: fixed;
3
+ z-index: 50;
4
+ inset: 0;
5
+ background-color: var(--background-overlay);
6
+
7
+ /* iOS 26+: Ensure the backdrop covers the entire visible viewport. */
8
+ @supports (-webkit-touch-callout: none) {
9
+ position: absolute;
10
+ }
11
+ }
12
+
13
+ .dialog__popup {
14
+ display: block;
15
+ position: fixed;
16
+ z-index: 51;
17
+ inset: 0;
18
+ isolation: isolate;
19
+ padding: var(--space-16);
20
+ height: 100%;
21
+ overflow: auto;
22
+ }
23
+
24
+ .dialog__popup_centered {
25
+ display: flex;
26
+ place-items: center;
27
+
28
+ @media screen and (width >= 512px) {
29
+ padding: var(--space-32);
30
+ padding-top: var(--space-64);
31
+ padding-bottom: var(--space-64);
32
+ }
33
+ }
34
+
35
+ .dialog__content {
36
+ position: relative;
37
+ flex-grow: 1;
38
+ flex-shrink: 1;
39
+ gap: var(--space-16);
40
+ margin: 0 auto;
41
+ outline: none;
42
+ box-shadow: var(--shadow-2);
43
+ border: 1px solid var(--outline-dimmest);
44
+ border-radius: var(--border-radius-default);
45
+ padding: var(--space-16);
46
+ width: 100%;
47
+ height: auto;
48
+ overflow: hidden;
49
+ }
50
+
51
+ .dialog__content_width_sm {
52
+ max-width: 384px;
53
+ }
54
+
55
+ .dialog__content_width_md {
56
+ max-width: 512px;
57
+ }
58
+
59
+ .dialog__content_width_lg {
60
+ max-width: 872px;
61
+ }
62
+
63
+ .dialog__header {
64
+ gap: var(--space-4);
65
+ }
66
+
67
+ .dialog__close {
68
+ position: absolute;
69
+ top: var(--space-8);
70
+ right: var(--space-8);
71
+ }
@@ -0,0 +1,29 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { Dialog } from ".";
4
+ import { Button } from "../button";
5
+
6
+ const meta = {
7
+ title: "Dialog/Standard Dialog",
8
+ component: Dialog,
9
+ parameters: { layout: "centered" },
10
+ } satisfies Meta<typeof Dialog>;
11
+
12
+ export default meta;
13
+
14
+ type Story = StoryObj<typeof meta>;
15
+
16
+ export const Default: Story = {
17
+ args: {
18
+ title: "On color",
19
+ description:
20
+ "Lots of different, solid, highly saturated colors all over the UI. This makes it hard to tell what's important, and means users will be easily distracted.",
21
+ },
22
+ render: (args) => (
23
+ <Dialog
24
+ closable
25
+ trigger={<Button leftIcon="palette-line" children="Open Dialog" />}
26
+ {...args}
27
+ />
28
+ ),
29
+ };