@stainless-api/ui-primitives 0.1.0-beta.2 → 0.1.0-beta.20

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 (51) hide show
  1. package/.turbo/turbo-build.log +35 -0
  2. package/CHANGELOG.md +112 -0
  3. package/dist/Accordion-CJL9SWwS.d.ts +26 -0
  4. package/dist/Accordion-DLQE3Td6.js +31 -0
  5. package/dist/Button-DBhd6kU7.js +51 -0
  6. package/dist/Button-DwndlytB.d.ts +38 -0
  7. package/dist/Callout-CMz3Yl_5.d.ts +18 -0
  8. package/dist/Callout-UZQRuCQ5.js +28 -0
  9. package/dist/DropdownButton-DoYDi8tB.js +82 -0
  10. package/dist/DropdownButton-zcvep_xH.d.ts +50 -0
  11. package/dist/components/Accordion.d.ts +2 -0
  12. package/dist/components/Accordion.js +3 -0
  13. package/dist/components/Button.d.ts +2 -0
  14. package/dist/components/Button.js +3 -0
  15. package/dist/components/Callout.d.ts +2 -0
  16. package/dist/components/Callout.js +3 -0
  17. package/dist/components/DropdownButton.d.ts +2 -0
  18. package/dist/components/DropdownButton.js +3 -0
  19. package/dist/index.d.ts +5 -0
  20. package/dist/index.js +6 -0
  21. package/dist/scripts/index.d.ts +12 -0
  22. package/dist/scripts/index.js +33 -0
  23. package/dist/styles.css +1181 -0
  24. package/dist/styles.js +1 -0
  25. package/package.json +9 -9
  26. package/src/components/Accordion.tsx +41 -0
  27. package/src/components/Button.tsx +1 -0
  28. package/src/components/accordion.css +145 -0
  29. package/src/components/button.css +165 -135
  30. package/src/components/callout.css +78 -57
  31. package/src/components/dropdown/Dropdown.tsx +51 -0
  32. package/src/components/dropdown/DropdownButton.tsx +54 -0
  33. package/src/components/dropdown/DropdownMenu.tsx +113 -0
  34. package/src/components/dropdown/dropdown.css +231 -0
  35. package/src/index.ts +3 -2
  36. package/src/scripts/dropdown-button.ts +20 -36
  37. package/src/scripts/dropdown.ts +193 -0
  38. package/src/scripts/index.ts +1 -0
  39. package/src/styles/layout.css +3 -1
  40. package/src/styles/scales.css +1 -1
  41. package/src/styles/starlight-compat.css +138 -107
  42. package/src/styles/swatches.css +2 -2
  43. package/src/styles/theme.css +2 -2
  44. package/src/styles/typography.css +117 -147
  45. package/src/styles.css +2 -2
  46. package/tsconfig.json +3 -7
  47. package/.env +0 -1
  48. package/src/components/DetailsGroup.tsx +0 -17
  49. package/src/components/DropdownButton.tsx +0 -98
  50. package/src/components/details.css +0 -126
  51. package/src/components/dropdown-button.css +0 -162
@@ -1,72 +1,93 @@
1
- @layer stl-ui {
2
- .stl-ui-callout {
3
- --stl-ui-callout-background-color: var(--stl-ui-muted-background);
4
- --stl-ui-callout-border-color: var(--stl-ui-border);
5
- --stl-ui-callout-accent-color: var(--stl-ui-foreground-muted);
1
+ .stl-ui-callout {
2
+ --stl-ui-callout-background-color: var(--stl-ui-muted-background);
3
+ --stl-ui-callout-border-color: var(--stl-ui-border);
4
+ --stl-ui-callout-accent-color: var(--stl-ui-foreground-muted);
5
+ --stl-ui-callout-strong-color: var(--stl-ui-callout-accent-color);
6
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-gray-gray-6);
6
7
 
7
- border: 1px solid var(--stl-ui-callout-border-color);
8
- background-color: var(--stl-ui-callout-background-color);
9
- border-radius: var(--stl-ui-layout-border-radius);
10
- padding: 12px;
11
- line-height: var(--stl-ui-typography-line-height);
12
- font-weight: 400;
13
- font-size: var(--stl-ui-typography-text-body);
8
+ border: 1px solid var(--stl-ui-callout-border-color);
9
+ background-color: var(--stl-ui-callout-background-color);
10
+ border-radius: var(--stl-ui-layout-border-radius);
11
+ padding: 12px;
12
+ line-height: var(--stl-ui-typography-line-height);
13
+ font-weight: 400;
14
+ font-size: var(--stl-ui-typography-text-body);
14
15
 
15
- display: flex;
16
- align-items: flex-start;
17
- gap: 8px;
18
- }
16
+ display: flex;
17
+ align-items: flex-start;
18
+ gap: 8px;
19
19
 
20
- .stl-ui-callout--info {
21
- --stl-ui-callout-background-color: var(--stl-ui-muted-background);
22
- --stl-ui-callout-border-color: var(--stl-ui-border);
23
- --stl-ui-callout-accent-color: var(--stl-ui-foreground-muted);
20
+ :where(a) {
21
+ color: var(--stl-ui-callout-strong-color);
22
+ text-decoration-color: rgba(from currentColor r g b / 0.4);
23
+ transition: text-decoration-color 0.1s ease;
24
+ &:hover {
25
+ text-decoration-color: rgba(from currentColor r g b / 0.8);
26
+ }
24
27
  }
25
28
 
26
- .stl-ui-callout--note {
27
- --stl-ui-callout-background-color: var(--stl-ui-swatch-blue-faint);
28
- --stl-ui-callout-border-color: var(--stl-ui-swatch-blue-muted);
29
- --stl-ui-callout-accent-color: var(--stl-ui-swatch-blue-base);
29
+ ::selection {
30
+ background-color: var(--stl-ui-callout-selection-background);
31
+ color: var(--stl-ui-foreground);
30
32
  }
33
+ }
31
34
 
32
- .stl-ui-callout--tip {
33
- --stl-ui-callout-background-color: var(--stl-ui-swatch-purple-faint);
34
- --stl-ui-callout-border-color: var(--stl-ui-swatch-purple-muted);
35
- --stl-ui-callout-accent-color: var(--stl-ui-swatch-purple-base);
36
- }
35
+ .stl-ui-callout--info {
36
+ --stl-ui-callout-background-color: var(--stl-ui-muted-background);
37
+ --stl-ui-callout-border-color: var(--stl-ui-border);
38
+ --stl-ui-callout-accent-color: var(--stl-ui-foreground-muted);
39
+ --stl-ui-callout-strong-color: var(--stl-ui-foreground);
40
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-gray-gray-6);
41
+ }
37
42
 
38
- .stl-ui-callout--success {
39
- --stl-ui-callout-background-color: var(--stl-ui-swatch-green-faint);
40
- --stl-ui-callout-border-color: var(--stl-ui-swatch-green-muted);
41
- --stl-ui-callout-accent-color: var(--stl-ui-swatch-green-base);
42
- }
43
+ .stl-ui-callout--note {
44
+ --stl-ui-callout-background-color: var(--stl-ui-swatch-blue-faint);
45
+ --stl-ui-callout-border-color: var(--stl-ui-swatch-blue-muted);
46
+ --stl-ui-callout-accent-color: var(--stl-ui-swatch-blue-base);
47
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-blue-muted);
48
+ }
43
49
 
44
- .stl-ui-callout--warning {
45
- --stl-ui-callout-background-color: var(--stl-ui-swatch-yellow-faint);
46
- --stl-ui-callout-border-color: var(--stl-ui-swatch-yellow-muted);
47
- --stl-ui-callout-accent-color: var(--stl-ui-swatch-yellow-base);
48
- }
50
+ .stl-ui-callout--tip {
51
+ --stl-ui-callout-background-color: var(--stl-ui-swatch-purple-faint);
52
+ --stl-ui-callout-border-color: var(--stl-ui-swatch-purple-muted);
53
+ --stl-ui-callout-accent-color: var(--stl-ui-swatch-purple-base);
54
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-purple-muted);
55
+ }
49
56
 
50
- .stl-ui-callout--danger {
51
- --stl-ui-callout-background-color: var(--stl-ui-swatch-red-faint);
52
- --stl-ui-callout-border-color: var(--stl-ui-swatch-red-muted);
53
- --stl-ui-callout-accent-color: var(--stl-ui-swatch-red-base);
54
- }
57
+ .stl-ui-callout--success {
58
+ --stl-ui-callout-background-color: var(--stl-ui-swatch-green-faint);
59
+ --stl-ui-callout-border-color: var(--stl-ui-swatch-green-muted);
60
+ --stl-ui-callout-accent-color: var(--stl-ui-swatch-green-base);
61
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-green-muted);
62
+ }
55
63
 
56
- .stl-ui-callout__icon {
57
- color: var(--stl-ui-callout-accent-color);
58
- flex-shrink: 0;
59
- width: 1em;
60
- height: 1em;
61
- margin: calc((1lh - 1em) / 2);
62
- }
64
+ .stl-ui-callout--warning {
65
+ --stl-ui-callout-background-color: var(--stl-ui-swatch-yellow-faint);
66
+ --stl-ui-callout-border-color: var(--stl-ui-swatch-yellow-muted);
67
+ --stl-ui-callout-accent-color: var(--stl-ui-swatch-yellow-base);
68
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-yellow-muted);
69
+ }
63
70
 
64
- .stl-ui-callout__content {
65
- flex: 1;
66
- margin-top: 0;
71
+ .stl-ui-callout--danger {
72
+ --stl-ui-callout-background-color: var(--stl-ui-swatch-red-faint);
73
+ --stl-ui-callout-border-color: var(--stl-ui-swatch-red-muted);
74
+ --stl-ui-callout-accent-color: var(--stl-ui-swatch-red-base);
75
+ --stl-ui-callout-selection-background: var(--stl-ui-swatch-red-muted);
76
+ }
67
77
 
68
- & > :first-child {
69
- margin-top: 0;
70
- }
78
+ .stl-ui-callout__icon {
79
+ color: var(--stl-ui-callout-accent-color);
80
+ flex-shrink: 0;
81
+ width: 1em;
82
+ height: 1em;
83
+ margin: calc((1lh - 1em) / 2);
84
+ }
85
+
86
+ .stl-ui-callout__content {
87
+ flex: 1;
88
+ margin-top: 0;
89
+
90
+ & > :first-child {
91
+ margin-top: 0;
71
92
  }
72
93
  }
@@ -0,0 +1,51 @@
1
+ import clsx from 'clsx';
2
+ import type { ComponentProps } from 'react';
3
+ import { Menu } from './DropdownMenu';
4
+
5
+ function Trigger({ className, ...props }: ComponentProps<'button'>) {
6
+ return (
7
+ <button {...props} data-part="trigger" className={clsx('stl-ui-dropdown__button', className)}>
8
+ {props.children}
9
+ </button>
10
+ );
11
+ }
12
+
13
+ function TriggerSelectedItem({ className, ...props }: ComponentProps<'div'>) {
14
+ return (
15
+ <div
16
+ {...props}
17
+ data-part="trigger-selected"
18
+ className={clsx('stl-ui-dropdown__trigger-selected', className)}
19
+ />
20
+ );
21
+ }
22
+
23
+ function TriggerIcon({ className, ...props }: ComponentProps<'span'>) {
24
+ return (
25
+ <span {...props} data-part="trigger-icon" className={clsx('stl-ui-dropdown__trigger-icon', className)} />
26
+ );
27
+ }
28
+
29
+ function Icon({ className, ...props }: ComponentProps<'div'>) {
30
+ return <div {...props} data-part="item-icon" className={clsx('stl-ui-dropdown__icon', className)} />;
31
+ }
32
+
33
+ export function Dropdown({ className, ...props }: ComponentProps<'div'>) {
34
+ return (
35
+ <div
36
+ {...props}
37
+ aria-haspopup="listbox"
38
+ aria-expanded="false"
39
+ className={clsx('stl-ui-dropdown stl-ui-not-prose not-content', className)}
40
+ />
41
+ );
42
+ }
43
+
44
+ Dropdown.Menu = Menu;
45
+ Dropdown.MenuItem = Menu.Item;
46
+ Dropdown.MenuItemText = Menu.ItemText;
47
+ Dropdown.MenuItemTemplate = Menu.ItemTemplate;
48
+ Dropdown.Trigger = Trigger;
49
+ Dropdown.TriggerSelectedItem = TriggerSelectedItem;
50
+ Dropdown.TriggerIcon = TriggerIcon;
51
+ Dropdown.Icon = Icon;
@@ -0,0 +1,54 @@
1
+ import clsx from 'clsx';
2
+ import { ChevronsUpDown } from 'lucide-react';
3
+ import type { ComponentProps } from 'react';
4
+ import { Menu } from './DropdownMenu';
5
+
6
+ function PrimaryActionText({ children }: { children?: React.ReactNode }) {
7
+ return <span data-part="primary-action-text">{children}</span>;
8
+ }
9
+
10
+ function PrimaryAction({ className, ...props }: ComponentProps<'button'>) {
11
+ return (
12
+ <button
13
+ {...props}
14
+ data-part="primary-action"
15
+ className={clsx('stl-ui-dropdown__button stl-ui-dropdown-button--action', className)}
16
+ />
17
+ );
18
+ }
19
+
20
+ function Trigger({ className, ...props }: ComponentProps<'button'>) {
21
+ return (
22
+ <button
23
+ {...props}
24
+ type="button"
25
+ data-part="trigger"
26
+ className={clsx('stl-ui-dropdown__button stl-ui-dropdown-button__trigger', className)}
27
+ >
28
+ <ChevronsUpDown size={16} />
29
+ </button>
30
+ );
31
+ }
32
+
33
+ function Icon({ className, ...props }: ComponentProps<'div'>) {
34
+ return <div data-part="item-icon" {...props} className={clsx('stl-ui-dropdown__icon', className)} />;
35
+ }
36
+
37
+ export function DropdownButton({ className, ...props }: ComponentProps<'div'>) {
38
+ return (
39
+ <div
40
+ {...props}
41
+ aria-haspopup="listbox"
42
+ aria-expanded="false"
43
+ className={clsx('stl-ui-dropdown stl-ui-not-prose not-content', className)}
44
+ />
45
+ );
46
+ }
47
+
48
+ DropdownButton.Menu = Menu;
49
+ DropdownButton.MenuItem = Menu.Item;
50
+ DropdownButton.MenuItemText = Menu.ItemText;
51
+ DropdownButton.PrimaryAction = PrimaryAction;
52
+ DropdownButton.PrimaryActionText = PrimaryActionText;
53
+ DropdownButton.Trigger = Trigger;
54
+ DropdownButton.Icon = Icon;
@@ -0,0 +1,113 @@
1
+ import clsx from 'clsx';
2
+ import { CheckIcon, ExternalLink } from 'lucide-react';
3
+ import type { ComponentProps } from 'react';
4
+
5
+ export function Menu({ className, ...props }: ComponentProps<'div'>) {
6
+ return (
7
+ <div
8
+ {...props}
9
+ role="listbox"
10
+ data-state="closed"
11
+ data-part="menu"
12
+ className={clsx('stl-ui-dropdown-menu', className)}
13
+ />
14
+ );
15
+ }
16
+
17
+ function MenuItemText({ className, subtle, ...props }: ComponentProps<'span'> & { subtle?: boolean }) {
18
+ return (
19
+ <span
20
+ {...props}
21
+ data-part="item-text"
22
+ className={clsx(
23
+ `stl-ui-dropdown-menu__item-text`,
24
+ {
25
+ 'stl-ui-dropdown-menu__item-text--subtle': subtle,
26
+ },
27
+ className,
28
+ )}
29
+ />
30
+ );
31
+ }
32
+
33
+ type MenuItemBaseProps = {
34
+ children?: React.ReactNode;
35
+ value: string;
36
+ isExternalLink?: boolean;
37
+ isSelected?: boolean;
38
+ };
39
+
40
+ type MenuItemWithHref = MenuItemBaseProps &
41
+ ComponentProps<'a'> & {
42
+ href: string;
43
+ };
44
+
45
+ type MenuItemWithoutHref = MenuItemBaseProps &
46
+ ComponentProps<'button'> & {
47
+ href?: never;
48
+ };
49
+
50
+ type MenuItemProps = MenuItemWithHref | MenuItemWithoutHref;
51
+
52
+ function MenuItem({ children, value, href, isExternalLink, isSelected, ...props }: MenuItemProps) {
53
+ const inner = (
54
+ <>
55
+ <div className="stl-ui-dropdown-menu__item-content">{children}</div>
56
+ {isSelected && (
57
+ <div className="stl-ui-dropdown-menu__item-icon" data-part="item-selected-icon">
58
+ <CheckIcon size={16} />
59
+ </div>
60
+ )}
61
+ {isExternalLink && (
62
+ <div className="stl-ui-dropdown-menu__item-subtle-icon" data-part="item-external-link-icon">
63
+ <ExternalLink size={16} />
64
+ </div>
65
+ )}
66
+ </>
67
+ );
68
+
69
+ if (href) {
70
+ return (
71
+ <a
72
+ role="option"
73
+ data-part="item"
74
+ data-value={value}
75
+ aria-selected={isSelected}
76
+ href={href}
77
+ {...(props as ComponentProps<'a'>)}
78
+ className={clsx('stl-ui-dropdown-menu__item', 'stl-ui-dropdown-menu__item-link', props.className)}
79
+ >
80
+ {inner}
81
+ </a>
82
+ );
83
+ }
84
+
85
+ return (
86
+ <button
87
+ {...(props as ComponentProps<'button'>)}
88
+ role="option"
89
+ data-part="item"
90
+ data-value={value}
91
+ aria-selected={isSelected}
92
+ className={clsx('stl-ui-dropdown-menu__item', props.className)}
93
+ >
94
+ {inner}
95
+ </button>
96
+ );
97
+ }
98
+
99
+ /**
100
+ * A template component that defines the content to be displayed in the dropdown trigger
101
+ * when a menu item is selected. This template is used to customize the appearance of
102
+ * the selected item in the trigger button.
103
+ *
104
+ * @param props - Standard HTML template element props
105
+ * @returns A template element marked with the "selected-template" data part
106
+ */
107
+ function MenuItemTemplate({ ...props }: ComponentProps<'template'>) {
108
+ return <template data-part="selected-template" {...props} />;
109
+ }
110
+
111
+ Menu.Item = MenuItem;
112
+ Menu.ItemText = MenuItemText;
113
+ Menu.ItemTemplate = MenuItemTemplate;
@@ -0,0 +1,231 @@
1
+ .stl-ui-dropdown {
2
+ --stl-ui-dropdown-button-color: var(--stl-ui-foreground);
3
+ --stl-ui-dropdown-button-background-color: var(--stl-ui-card-background);
4
+ --stl-ui-dropdown-button-border-color: var(--stl-ui-border);
5
+ --stl-ui-dropdown-button-border-radius: var(--stl-ui-layout-border-radius-sml);
6
+ --stl-ui-dropdown-button-font-size: var(--stl-ui-type-scale-text-sm);
7
+
8
+ --stl-ui-dropdown-button-height: 32px;
9
+ --stl-ui-dropdown-button-padding: 0 10px;
10
+ --stl-ui-dropdown-button-line-height: 100%;
11
+ --stl-ui-dropdown-button-font-weight: 500;
12
+
13
+ position: relative;
14
+ display: inline-flex;
15
+ align-items: center;
16
+
17
+ color: var(--stl-ui-dropdown-button-color);
18
+ gap: 0;
19
+ font-size: var(--stl-ui-dropdown-button-font-size);
20
+
21
+ hr {
22
+ --stl-ui-dropdown-button__divider-height: 1px;
23
+ --stl-ui-dropdown-button__divider-color: var(--stl-ui-border);
24
+
25
+ height: var(--stl-ui-dropdown-button__divider-height);
26
+ background-color: var(--stl-ui-dropdown-button__divider-color);
27
+ border: none;
28
+
29
+ margin: 4px 0;
30
+ width: calc(100% + 8px);
31
+ transform: translateX(-4px);
32
+ }
33
+
34
+ .stl-ui-dropdown__button {
35
+ background-color: var(--stl-ui-dropdown-button-background-color);
36
+
37
+ border: 1px solid var(--stl-ui-dropdown-button-border-color);
38
+ border-radius: var(--stl-ui-dropdown-button-border-radius);
39
+ height: var(--stl-ui-dropdown-button-height);
40
+ padding: var(--stl-ui-dropdown-button-padding);
41
+ line-height: var(--stl-ui-dropdown-button-line-height);
42
+ font-weight: var(--stl-ui-dropdown-button-font-weight);
43
+ cursor: pointer;
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ color: inherit;
48
+
49
+ &:focus {
50
+ outline: 1px auto Highlight;
51
+ }
52
+
53
+ &:hover {
54
+ background-color: oklch(from var(--stl-ui-foreground) l c h / 0.05);
55
+ border-color: var(--stl-ui-border-emphasis);
56
+ }
57
+
58
+ .stl-ui-dropdown__trigger-icon {
59
+ margin-right: -4px;
60
+ margin-left: 4px;
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ }
65
+ }
66
+
67
+ .stl-ui-dropdown__icon {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+
72
+ svg {
73
+ width: 16px;
74
+ height: 16px;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Dropdown Button
80
+ */
81
+ .stl-ui-dropdown-button--action {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 8px;
85
+ border-top-right-radius: 0;
86
+ border-bottom-right-radius: 0;
87
+ border-right: none;
88
+
89
+ &:hover {
90
+ background-color: oklch(from var(--stl-ui-foreground) l c h / 0.05);
91
+ border-color: var(--stl-ui-border-emphasis);
92
+ }
93
+
94
+ &.disabled {
95
+ cursor: not-allowed;
96
+ background-color: oklch(from var(--stl-ui-foreground) l c h / 0.05);
97
+ }
98
+ }
99
+
100
+ .stl-ui-dropdown-button__trigger {
101
+ border-left: 1px solid var(--stl-ui-border);
102
+ border-radius: 0;
103
+ border-top-right-radius: var(--stl-ui-dropdown-button-border-radius);
104
+ border-bottom-right-radius: var(--stl-ui-dropdown-button-border-radius);
105
+
106
+ &:hover {
107
+ background-color: oklch(from var(--stl-ui-foreground) l c h / 0.05);
108
+ border-color: var(--stl-ui-border-emphasis);
109
+ }
110
+ }
111
+
112
+ .stl-ui-dropdown__trigger-selected {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 8px;
116
+ }
117
+
118
+ /**
119
+ * Dropdown Menu
120
+ */
121
+ .stl-ui-dropdown-menu {
122
+ --stl-ui-dropdown-menu-background-color: var(--stl-ui-card-background);
123
+ --stl-ui-dropdown-menu-border-color: var(--stl-ui-border);
124
+ --stl-ui-dropdown-menu-box-shadow: var(--stl-ui-shadow-md);
125
+ --stl-ui-dropdown-menu-border-radius: var(--stl-ui-layout-border-radius-sml);
126
+
127
+ background-color: var(--stl-ui-dropdown-menu-background-color);
128
+ border: 1px solid var(--stl-ui-dropdown-menu-border-color);
129
+ box-shadow: var(--stl-ui-dropdown-menu-box-shadow);
130
+ border-radius: var(--stl-ui-dropdown-menu-border-radius);
131
+
132
+ position: absolute;
133
+ right: 0;
134
+ z-index: 1000;
135
+ min-width: 100%;
136
+ padding: 4px;
137
+ display: none;
138
+
139
+ &.stl-ui-dropdown-menu--above {
140
+ top: auto;
141
+ bottom: 100%;
142
+ margin-bottom: 4px;
143
+ }
144
+
145
+ &.stl-ui-dropdown-menu--below {
146
+ top: 100%;
147
+ bottom: auto;
148
+ margin-top: 4px;
149
+ }
150
+
151
+ &[data-state='open'] {
152
+ display: block;
153
+ }
154
+
155
+ .stl-ui-dropdown-menu__item {
156
+ --stl-ui-dropdown-menu__item-border-radius: var(--stl-ui-dropdown-button-border-radius);
157
+ --stl-ui-dropdown-menu__item-height: var(--stl-ui-dropdown-button-height);
158
+ --stl-ui-dropdown-menu__item-line-height: var(--stl-ui-dropdown-button-line-height);
159
+ --stl-ui-dropdown-menu__item-hover-background-color: oklch(from var(--stl-ui-foreground) l c h / 0.05);
160
+
161
+ border-radius: var(--stl-ui-dropdown-menu__item-border-radius);
162
+ height: var(--stl-ui-dropdown-menu__item-height);
163
+ line-height: var(--stl-ui-dropdown-menu__item-line-height);
164
+
165
+ background: transparent;
166
+ border: none;
167
+
168
+ padding: 8px;
169
+ cursor: pointer;
170
+ display: flex;
171
+ align-items: center;
172
+ justify-content: space-between;
173
+ gap: 16px;
174
+ width: 100%;
175
+
176
+ &:hover,
177
+ &:focus-visible {
178
+ background-color: var(--stl-ui-dropdown-menu__item-hover-background-color);
179
+ }
180
+
181
+ &:hover,
182
+ &:focus-visible,
183
+ &:focus {
184
+ color: var(--stl-ui-foreground);
185
+ }
186
+
187
+ &.stl-ui-dropdown-menu__item-link {
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: space-between;
191
+ gap: 16px;
192
+ width: 100%;
193
+
194
+ &:hover {
195
+ color: var(--stl-ui-foreground);
196
+ }
197
+ }
198
+
199
+ .stl-ui-dropdown-menu__item-icon {
200
+ display: flex;
201
+ }
202
+
203
+ .stl-ui-dropdown-menu__item-content {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 8px;
207
+ }
208
+
209
+ .stl-ui-dropdown-menu__item-text {
210
+ white-space: nowrap;
211
+ }
212
+
213
+ .stl-ui-dropdown-menu__item-text--subtle {
214
+ color: var(--stl-ui-foreground-muted);
215
+ }
216
+
217
+ strong {
218
+ color: var(--stl-ui-foreground);
219
+ font-weight: 500;
220
+ }
221
+
222
+ .stl-ui-dropdown-menu__item-subtle-icon {
223
+ --stl-ui-dropdown-menu__item-subtle-icon-color: oklch(from var(--stl-ui-foreground) l c h / 0.25);
224
+
225
+ svg {
226
+ color: var(--stl-ui-dropdown-menu__item-subtle-icon-color);
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './components/Button';
2
- export * from './components/DropdownButton';
2
+ export * from './components/dropdown/Dropdown';
3
+ export * from './components/dropdown/DropdownButton';
3
4
  export * from './components/Callout';
4
- export * from './components/DetailsGroup';
5
+ export * from './components/Accordion';
@@ -1,55 +1,39 @@
1
+ import { initDropdown } from './dropdown';
2
+
1
3
  export function initDropdownButton({
2
- dropdownId,
4
+ dropdown,
3
5
  onSelect,
4
6
  onPrimaryAction,
5
7
  }: {
6
- dropdownId: string;
8
+ dropdown: Element;
7
9
  onSelect: (value: string) => void;
8
10
  onPrimaryAction: (primaryActionElement: Element) => void;
9
11
  }) {
10
- const dropdown = document.getElementById(dropdownId);
11
- if (!dropdown) return;
12
-
13
12
  const trigger = dropdown.querySelector('[data-part="trigger"]') as HTMLButtonElement | null;
14
13
  const menu = dropdown.querySelector('[data-part="menu"]') as HTMLElement | null;
15
14
  const primaryAction = dropdown.querySelector('[data-part="primary-action"]');
16
15
 
17
- if (!trigger || !menu || !primaryAction) return;
18
-
19
- let isOpen = false;
20
-
21
- function toggleDropdown() {
22
- if (!trigger || !menu) return;
23
- isOpen = !isOpen;
24
- menu.dataset.state = isOpen ? 'open' : 'closed';
25
- trigger.setAttribute('aria-expanded', String(isOpen));
16
+ if (!trigger) {
17
+ console.error('Dropdown trigger not found');
18
+ return;
26
19
  }
27
20
 
28
- trigger.addEventListener('click', toggleDropdown);
29
-
30
- document.addEventListener('click', (event) => {
31
- if (!isOpen) return;
32
- if (!dropdown.contains(event.target as Node)) {
33
- toggleDropdown();
34
- }
35
-
36
- if (primaryAction && primaryAction.contains(event.target as Node)) {
37
- toggleDropdown();
38
- }
39
- });
21
+ if (!menu) {
22
+ console.error('Dropdown menu not found');
23
+ return;
24
+ }
40
25
 
41
- const items = dropdown.querySelectorAll('[data-part="item"]');
42
- items.forEach((item) => {
43
- item.addEventListener('click', () => {
44
- const value = item.getAttribute('data-value');
45
- if (value) {
46
- onSelect(value);
47
- }
48
- toggleDropdown();
49
- });
50
- });
26
+ if (!primaryAction) {
27
+ console.error('Dropdown primary action not found');
28
+ return;
29
+ }
51
30
 
52
31
  primaryAction.addEventListener('click', () => {
53
32
  onPrimaryAction(primaryAction);
54
33
  });
34
+
35
+ initDropdown({
36
+ root: dropdown,
37
+ onSelect,
38
+ });
55
39
  }