@duro-app/ui 0.39.0 → 0.41.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/dist/components/ActionBar/styles.css.d.ts +2 -2
- package/dist/components/ActionBar/styles.css.d.ts.map +1 -1
- package/dist/components/Checkbox/styles.css.d.ts +2 -2
- package/dist/components/Checkbox/styles.css.d.ts.map +1 -1
- package/dist/components/ColorMode/styles.css.d.ts +2 -2
- package/dist/components/ColorMode/styles.css.d.ts.map +1 -1
- package/dist/components/Combobox/styles.css.d.ts +2 -2
- package/dist/components/Combobox/styles.css.d.ts.map +1 -1
- package/dist/components/Dialog/styles.css.d.ts +5 -5
- package/dist/components/Dialog/styles.css.d.ts.map +1 -1
- package/dist/components/Drawer/styles.css.d.ts +13 -13
- package/dist/components/Drawer/styles.css.d.ts.map +1 -1
- package/dist/components/Icon/Icon.d.ts +1 -1
- package/dist/components/Icon/Icon.d.ts.map +1 -1
- package/dist/components/Input/styles.css.d.ts +2 -2
- package/dist/components/Input/styles.css.d.ts.map +1 -1
- package/dist/components/InputGroup/styles.css.d.ts +4 -4
- package/dist/components/InputGroup/styles.css.d.ts.map +1 -1
- package/dist/components/LinkButton/styles.css.d.ts +2 -2
- package/dist/components/LinkButton/styles.css.d.ts.map +1 -1
- package/dist/components/Menu/styles.css.d.ts +2 -2
- package/dist/components/Menu/styles.css.d.ts.map +1 -1
- package/dist/components/RadioGroup/styles.css.d.ts +2 -2
- package/dist/components/RadioGroup/styles.css.d.ts.map +1 -1
- package/dist/components/ScrollArea/styles.css.d.ts +3 -3
- package/dist/components/ScrollArea/styles.css.d.ts.map +1 -1
- package/dist/components/Select/styles.css.d.ts +2 -2
- package/dist/components/Select/styles.css.d.ts.map +1 -1
- package/dist/components/SideNav/SideNav.d.ts +16 -1
- package/dist/components/SideNav/SideNav.d.ts.map +1 -1
- package/dist/components/SideNav/SideNav.meta.d.ts.map +1 -1
- package/dist/components/SideNav/SideNav.stories.d.ts +1 -0
- package/dist/components/SideNav/SideNav.stories.d.ts.map +1 -1
- package/dist/components/SideNav/styles.css.d.ts +54 -6
- package/dist/components/SideNav/styles.css.d.ts.map +1 -1
- package/dist/components/Switch/styles.css.d.ts +4 -4
- package/dist/components/Switch/styles.css.d.ts.map +1 -1
- package/dist/components/Table/styles.css.d.ts +2 -2
- package/dist/components/Table/styles.css.d.ts.map +1 -1
- package/dist/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/components/Tabs/TabsContext.d.ts +2 -0
- package/dist/components/Tabs/TabsContext.d.ts.map +1 -1
- package/dist/components/Tabs/styles.css.d.ts +33 -2
- package/dist/components/Tabs/styles.css.d.ts.map +1 -1
- package/dist/components/Tabs/useTabsRoot.d.ts.map +1 -1
- package/dist/components/TagGroup/styles.css.d.ts +2 -2
- package/dist/components/TagGroup/styles.css.d.ts.map +1 -1
- package/dist/components/Textarea/styles.css.d.ts +2 -2
- package/dist/components/Textarea/styles.css.d.ts.map +1 -1
- package/dist/components/Toast/styles.css.d.ts +1 -1
- package/dist/components/Toast/styles.css.d.ts.map +1 -1
- package/dist/components/Toggle/styles.css.d.ts +2 -2
- package/dist/components/Toggle/styles.css.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +2515 -2329
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/ActionBar/styles.css.ts +3 -2
- package/src/components/Checkbox/styles.css.ts +3 -2
- package/src/components/ColorMode/styles.css.ts +3 -2
- package/src/components/Combobox/styles.css.ts +3 -2
- package/src/components/Dialog/styles.css.ts +6 -5
- package/src/components/Drawer/styles.css.ts +14 -13
- package/src/components/Icon/Icon.tsx +46 -0
- package/src/components/Input/styles.css.ts +3 -2
- package/src/components/InputGroup/styles.css.ts +5 -4
- package/src/components/LinkButton/styles.css.ts +3 -2
- package/src/components/Menu/styles.css.ts +3 -2
- package/src/components/RadioGroup/styles.css.ts +3 -2
- package/src/components/ScrollArea/styles.css.ts +4 -3
- package/src/components/Select/styles.css.ts +3 -2
- package/src/components/SideNav/SideNav.meta.ts +17 -11
- package/src/components/SideNav/SideNav.stories.tsx +47 -3
- package/src/components/SideNav/SideNav.tsx +34 -2
- package/src/components/SideNav/styles.css.ts +58 -7
- package/src/components/Switch/styles.css.ts +5 -4
- package/src/components/Table/styles.css.ts +3 -2
- package/src/components/Tabs/Tabs.tsx +52 -3
- package/src/components/Tabs/TabsContext.ts +6 -0
- package/src/components/Tabs/styles.css.ts +41 -2
- package/src/components/Tabs/useTabsRoot.ts +12 -2
- package/src/components/TagGroup/styles.css.ts +3 -2
- package/src/components/Textarea/styles.css.ts +3 -2
- package/src/components/Toast/styles.css.ts +2 -1
- package/src/components/Toggle/styles.css.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duro-app/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@duro-app/tokens": "^0.
|
|
57
|
+
"@duro-app/tokens": "^0.41.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@babel/preset-typescript": "^7.28.0",
|
|
@@ -3,6 +3,7 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
6
7
|
|
|
7
8
|
export const styles = css.create({
|
|
8
9
|
overlay: {
|
|
@@ -63,8 +64,8 @@ export const styles = css.create({
|
|
|
63
64
|
},
|
|
64
65
|
cursor: 'pointer',
|
|
65
66
|
transitionProperty: 'color, background-color',
|
|
66
|
-
transitionDuration:
|
|
67
|
-
transitionTimingFunction:
|
|
67
|
+
transitionDuration: duration.fast,
|
|
68
|
+
transitionTimingFunction: easing.standard,
|
|
68
69
|
outlineWidth: {
|
|
69
70
|
default: 0,
|
|
70
71
|
':focus-visible': 2,
|
|
@@ -2,6 +2,7 @@ import {css} from 'react-strict-dom'
|
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
5
6
|
|
|
6
7
|
export const styles = css.create({
|
|
7
8
|
root: {
|
|
@@ -28,8 +29,8 @@ export const styles = css.create({
|
|
|
28
29
|
justifyContent: 'center',
|
|
29
30
|
flexShrink: 0,
|
|
30
31
|
transitionProperty: 'background-color, border-color',
|
|
31
|
-
transitionDuration:
|
|
32
|
-
transitionTimingFunction:
|
|
32
|
+
transitionDuration: duration.fast,
|
|
33
|
+
transitionTimingFunction: easing.standard,
|
|
33
34
|
},
|
|
34
35
|
boxUnchecked: {
|
|
35
36
|
backgroundColor: colors.bg,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {css} from 'react-strict-dom'
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
4
5
|
|
|
5
6
|
export const styles = css.create({
|
|
6
7
|
button: {
|
|
@@ -22,8 +23,8 @@ export const styles = css.create({
|
|
|
22
23
|
color: colors.text,
|
|
23
24
|
cursor: 'pointer',
|
|
24
25
|
transitionProperty: 'background-color, border-color, color',
|
|
25
|
-
transitionDuration:
|
|
26
|
-
transitionTimingFunction:
|
|
26
|
+
transitionDuration: duration.fast,
|
|
27
|
+
transitionTimingFunction: easing.standard,
|
|
27
28
|
outlineWidth: {default: 0, ':focus-visible': 2},
|
|
28
29
|
outlineStyle: 'solid',
|
|
29
30
|
outlineColor: colors.accent,
|
|
@@ -3,6 +3,7 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
|
+
import {duration} from '@duro-app/tokens/tokens/motion.css'
|
|
6
7
|
|
|
7
8
|
export const styles = css.create({
|
|
8
9
|
root: {
|
|
@@ -24,7 +25,7 @@ export const styles = css.create({
|
|
|
24
25
|
},
|
|
25
26
|
borderRadius: radii.sm,
|
|
26
27
|
transitionProperty: 'border-color',
|
|
27
|
-
transitionDuration:
|
|
28
|
+
transitionDuration: duration.fast,
|
|
28
29
|
},
|
|
29
30
|
inputWrapperFocused: {
|
|
30
31
|
borderColor: colors.accent,
|
|
@@ -94,7 +95,7 @@ export const styles = css.create({
|
|
|
94
95
|
cursor: 'pointer',
|
|
95
96
|
backgroundColor: 'transparent',
|
|
96
97
|
transitionProperty: 'background-color',
|
|
97
|
-
transitionDuration:
|
|
98
|
+
transitionDuration: duration.fast,
|
|
98
99
|
},
|
|
99
100
|
itemSelected: {
|
|
100
101
|
color: colors.accent,
|
|
@@ -3,6 +3,7 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
6
7
|
|
|
7
8
|
export const styles = css.create({
|
|
8
9
|
// --- Backdrop ---
|
|
@@ -19,7 +20,7 @@ export const styles = css.create({
|
|
|
19
20
|
to: {opacity: 1},
|
|
20
21
|
}),
|
|
21
22
|
animationDuration: '180ms',
|
|
22
|
-
animationTimingFunction:
|
|
23
|
+
animationTimingFunction: easing.easeOut,
|
|
23
24
|
animationFillMode: 'both',
|
|
24
25
|
},
|
|
25
26
|
backdropClosing: {
|
|
@@ -29,7 +30,7 @@ export const styles = css.create({
|
|
|
29
30
|
to: {opacity: 0},
|
|
30
31
|
}),
|
|
31
32
|
animationDuration: '140ms',
|
|
32
|
-
animationTimingFunction:
|
|
33
|
+
animationTimingFunction: easing.easeIn,
|
|
33
34
|
animationFillMode: 'both',
|
|
34
35
|
},
|
|
35
36
|
|
|
@@ -75,7 +76,7 @@ export const styles = css.create({
|
|
|
75
76
|
},
|
|
76
77
|
}),
|
|
77
78
|
animationDuration: '180ms',
|
|
78
|
-
animationTimingFunction:
|
|
79
|
+
animationTimingFunction: easing.easeOut,
|
|
79
80
|
animationFillMode: 'both',
|
|
80
81
|
},
|
|
81
82
|
popupClosing: {
|
|
@@ -91,7 +92,7 @@ export const styles = css.create({
|
|
|
91
92
|
},
|
|
92
93
|
}),
|
|
93
94
|
animationDuration: '140ms',
|
|
94
|
-
animationTimingFunction:
|
|
95
|
+
animationTimingFunction: easing.easeIn,
|
|
95
96
|
animationFillMode: 'both',
|
|
96
97
|
},
|
|
97
98
|
|
|
@@ -174,7 +175,7 @@ export const styles = css.create({
|
|
|
174
175
|
padding: 0,
|
|
175
176
|
flexShrink: 0,
|
|
176
177
|
transitionProperty: 'background-color',
|
|
177
|
-
transitionDuration:
|
|
178
|
+
transitionDuration: duration.fast,
|
|
178
179
|
},
|
|
179
180
|
|
|
180
181
|
// --- Inline wrapper (for Trigger / Close children) ---
|
|
@@ -3,6 +3,7 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
6
7
|
|
|
7
8
|
export const styles = css.create({
|
|
8
9
|
// --- Backdrop ---
|
|
@@ -17,8 +18,8 @@ export const styles = css.create({
|
|
|
17
18
|
from: {opacity: 0},
|
|
18
19
|
to: {opacity: 1},
|
|
19
20
|
}),
|
|
20
|
-
animationDuration:
|
|
21
|
-
animationTimingFunction:
|
|
21
|
+
animationDuration: duration.base,
|
|
22
|
+
animationTimingFunction: easing.easeOut,
|
|
22
23
|
animationFillMode: 'both',
|
|
23
24
|
},
|
|
24
25
|
backdropClosing: {
|
|
@@ -27,7 +28,7 @@ export const styles = css.create({
|
|
|
27
28
|
to: {opacity: 0},
|
|
28
29
|
}),
|
|
29
30
|
animationDuration: '160ms',
|
|
30
|
-
animationTimingFunction:
|
|
31
|
+
animationTimingFunction: easing.easeIn,
|
|
31
32
|
animationFillMode: 'both',
|
|
32
33
|
},
|
|
33
34
|
|
|
@@ -104,8 +105,8 @@ export const styles = css.create({
|
|
|
104
105
|
from: {transform: 'translateX(100%)'},
|
|
105
106
|
to: {transform: 'translateX(0)'},
|
|
106
107
|
}),
|
|
107
|
-
animationDuration:
|
|
108
|
-
animationTimingFunction:
|
|
108
|
+
animationDuration: duration.base,
|
|
109
|
+
animationTimingFunction: easing.easeOut,
|
|
109
110
|
animationFillMode: 'both',
|
|
110
111
|
},
|
|
111
112
|
slideOutRight: {
|
|
@@ -114,7 +115,7 @@ export const styles = css.create({
|
|
|
114
115
|
to: {transform: 'translateX(100%)'},
|
|
115
116
|
}),
|
|
116
117
|
animationDuration: '160ms',
|
|
117
|
-
animationTimingFunction:
|
|
118
|
+
animationTimingFunction: easing.easeIn,
|
|
118
119
|
animationFillMode: 'both',
|
|
119
120
|
},
|
|
120
121
|
|
|
@@ -124,8 +125,8 @@ export const styles = css.create({
|
|
|
124
125
|
from: {transform: 'translateX(-100%)'},
|
|
125
126
|
to: {transform: 'translateX(0)'},
|
|
126
127
|
}),
|
|
127
|
-
animationDuration:
|
|
128
|
-
animationTimingFunction:
|
|
128
|
+
animationDuration: duration.base,
|
|
129
|
+
animationTimingFunction: easing.easeOut,
|
|
129
130
|
animationFillMode: 'both',
|
|
130
131
|
},
|
|
131
132
|
slideOutLeft: {
|
|
@@ -134,7 +135,7 @@ export const styles = css.create({
|
|
|
134
135
|
to: {transform: 'translateX(-100%)'},
|
|
135
136
|
}),
|
|
136
137
|
animationDuration: '160ms',
|
|
137
|
-
animationTimingFunction:
|
|
138
|
+
animationTimingFunction: easing.easeIn,
|
|
138
139
|
animationFillMode: 'both',
|
|
139
140
|
},
|
|
140
141
|
|
|
@@ -144,8 +145,8 @@ export const styles = css.create({
|
|
|
144
145
|
from: {transform: 'translateY(100%)'},
|
|
145
146
|
to: {transform: 'translateY(0)'},
|
|
146
147
|
}),
|
|
147
|
-
animationDuration:
|
|
148
|
-
animationTimingFunction:
|
|
148
|
+
animationDuration: duration.base,
|
|
149
|
+
animationTimingFunction: easing.easeOut,
|
|
149
150
|
animationFillMode: 'both',
|
|
150
151
|
},
|
|
151
152
|
slideOutBottom: {
|
|
@@ -154,7 +155,7 @@ export const styles = css.create({
|
|
|
154
155
|
to: {transform: 'translateY(100%)'},
|
|
155
156
|
}),
|
|
156
157
|
animationDuration: '160ms',
|
|
157
|
-
animationTimingFunction:
|
|
158
|
+
animationTimingFunction: easing.easeIn,
|
|
158
159
|
animationFillMode: 'both',
|
|
159
160
|
},
|
|
160
161
|
|
|
@@ -232,7 +233,7 @@ export const styles = css.create({
|
|
|
232
233
|
padding: 0,
|
|
233
234
|
flexShrink: 0,
|
|
234
235
|
transitionProperty: 'background-color',
|
|
235
|
-
transitionDuration:
|
|
236
|
+
transitionDuration: duration.fast,
|
|
236
237
|
},
|
|
237
238
|
|
|
238
239
|
// --- Inline wrapper ---
|
|
@@ -21,6 +21,12 @@ export type IconName =
|
|
|
21
21
|
| 'git-branch'
|
|
22
22
|
| 'menu'
|
|
23
23
|
| 'pin'
|
|
24
|
+
// People / access / admin glyphs
|
|
25
|
+
| 'users'
|
|
26
|
+
| 'user-plus'
|
|
27
|
+
| 'mail'
|
|
28
|
+
| 'file-text'
|
|
29
|
+
| 'plug'
|
|
24
30
|
// Color-mode glyphs
|
|
25
31
|
| 'sun'
|
|
26
32
|
| 'moon'
|
|
@@ -153,6 +159,46 @@ const strokeIcons: Partial<Record<IconName, ReactNode>> = {
|
|
|
153
159
|
<line x1="3" y1="18" x2="21" y2="18" />
|
|
154
160
|
</>
|
|
155
161
|
),
|
|
162
|
+
// ---- people / access / admin ----
|
|
163
|
+
users: (
|
|
164
|
+
<>
|
|
165
|
+
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
166
|
+
<circle cx="9" cy="7" r="4" />
|
|
167
|
+
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
|
168
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
169
|
+
</>
|
|
170
|
+
),
|
|
171
|
+
'user-plus': (
|
|
172
|
+
<>
|
|
173
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
174
|
+
<circle cx="8.5" cy="7" r="4" />
|
|
175
|
+
<line x1="20" y1="8" x2="20" y2="14" />
|
|
176
|
+
<line x1="23" y1="11" x2="17" y2="11" />
|
|
177
|
+
</>
|
|
178
|
+
),
|
|
179
|
+
mail: (
|
|
180
|
+
<>
|
|
181
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
|
|
182
|
+
<polyline points="22,6 12,13 2,6" />
|
|
183
|
+
</>
|
|
184
|
+
),
|
|
185
|
+
'file-text': (
|
|
186
|
+
<>
|
|
187
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
188
|
+
<polyline points="14 2 14 8 20 8" />
|
|
189
|
+
<line x1="16" y1="13" x2="8" y2="13" />
|
|
190
|
+
<line x1="16" y1="17" x2="8" y2="17" />
|
|
191
|
+
<polyline points="10 9 9 9 8 9" />
|
|
192
|
+
</>
|
|
193
|
+
),
|
|
194
|
+
plug: (
|
|
195
|
+
<>
|
|
196
|
+
<path d="M12 22v-5" />
|
|
197
|
+
<path d="M9 8V2" />
|
|
198
|
+
<path d="M15 8V2" />
|
|
199
|
+
<path d="M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z" />
|
|
200
|
+
</>
|
|
201
|
+
),
|
|
156
202
|
pin: (
|
|
157
203
|
<>
|
|
158
204
|
<line x1="12" y1="17" x2="12" y2="22" />
|
|
@@ -2,6 +2,7 @@ import {css} from 'react-strict-dom'
|
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
5
6
|
|
|
6
7
|
export const styles = css.create({
|
|
7
8
|
base: {
|
|
@@ -19,8 +20,8 @@ export const styles = css.create({
|
|
|
19
20
|
borderStyle: 'solid',
|
|
20
21
|
borderRadius: radii.sm,
|
|
21
22
|
transitionProperty: 'border-color',
|
|
22
|
-
transitionDuration:
|
|
23
|
-
transitionTimingFunction:
|
|
23
|
+
transitionDuration: duration.fast,
|
|
24
|
+
transitionTimingFunction: easing.standard,
|
|
24
25
|
outlineWidth: {
|
|
25
26
|
default: 0,
|
|
26
27
|
':focus-visible': 2,
|
|
@@ -2,6 +2,7 @@ import {css} from 'react-strict-dom'
|
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
5
6
|
|
|
6
7
|
export const styles = css.create({
|
|
7
8
|
wrapper: {
|
|
@@ -16,8 +17,8 @@ export const styles = css.create({
|
|
|
16
17
|
borderRadius: radii.sm,
|
|
17
18
|
overflow: 'hidden',
|
|
18
19
|
transitionProperty: 'border-color',
|
|
19
|
-
transitionDuration:
|
|
20
|
-
transitionTimingFunction:
|
|
20
|
+
transitionDuration: duration.fast,
|
|
21
|
+
transitionTimingFunction: easing.standard,
|
|
21
22
|
},
|
|
22
23
|
addon: {
|
|
23
24
|
display: 'inline-flex',
|
|
@@ -51,8 +52,8 @@ export const styles = css.create({
|
|
|
51
52
|
':active': colors.bg,
|
|
52
53
|
},
|
|
53
54
|
transitionProperty: 'background-color',
|
|
54
|
-
transitionDuration:
|
|
55
|
-
transitionTimingFunction:
|
|
55
|
+
transitionDuration: duration.fast,
|
|
56
|
+
transitionTimingFunction: easing.standard,
|
|
56
57
|
},
|
|
57
58
|
addonDisabled: {
|
|
58
59
|
opacity: 0.4,
|
|
@@ -2,6 +2,7 @@ import {css} from 'react-strict-dom'
|
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
5
6
|
|
|
6
7
|
export const styles = css.create({
|
|
7
8
|
base: {
|
|
@@ -17,8 +18,8 @@ export const styles = css.create({
|
|
|
17
18
|
cursor: 'pointer',
|
|
18
19
|
textDecoration: 'none',
|
|
19
20
|
transitionProperty: 'background-color, border-color, color',
|
|
20
|
-
transitionDuration:
|
|
21
|
-
transitionTimingFunction:
|
|
21
|
+
transitionDuration: duration.fast,
|
|
22
|
+
transitionTimingFunction: easing.standard,
|
|
22
23
|
},
|
|
23
24
|
sizeDefault: {
|
|
24
25
|
paddingTop: spacing.sm,
|
|
@@ -3,6 +3,7 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
|
+
import {duration} from '@duro-app/tokens/tokens/motion.css'
|
|
6
7
|
|
|
7
8
|
export const styles = css.create({
|
|
8
9
|
root: {
|
|
@@ -30,7 +31,7 @@ export const styles = css.create({
|
|
|
30
31
|
borderRadius: radii.sm,
|
|
31
32
|
cursor: 'pointer',
|
|
32
33
|
transitionProperty: 'background-color, border-color',
|
|
33
|
-
transitionDuration:
|
|
34
|
+
transitionDuration: duration.fast,
|
|
34
35
|
},
|
|
35
36
|
backdrop: {
|
|
36
37
|
position: 'fixed',
|
|
@@ -74,7 +75,7 @@ export const styles = css.create({
|
|
|
74
75
|
cursor: 'pointer',
|
|
75
76
|
backgroundColor: 'transparent',
|
|
76
77
|
transitionProperty: 'background-color',
|
|
77
|
-
transitionDuration:
|
|
78
|
+
transitionDuration: duration.fast,
|
|
78
79
|
},
|
|
79
80
|
itemHighlighted: {
|
|
80
81
|
backgroundColor: colors.bgCardHover,
|
|
@@ -2,6 +2,7 @@ import {css} from 'react-strict-dom'
|
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
5
6
|
|
|
6
7
|
export const styles = css.create({
|
|
7
8
|
root: {
|
|
@@ -36,8 +37,8 @@ export const styles = css.create({
|
|
|
36
37
|
justifyContent: 'center',
|
|
37
38
|
flexShrink: 0,
|
|
38
39
|
transitionProperty: 'background-color, border-color',
|
|
39
|
-
transitionDuration:
|
|
40
|
-
transitionTimingFunction:
|
|
40
|
+
transitionDuration: duration.fast,
|
|
41
|
+
transitionTimingFunction: easing.standard,
|
|
41
42
|
},
|
|
42
43
|
circleUnchecked: {
|
|
43
44
|
backgroundColor: colors.bg,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {css} from 'react-strict-dom'
|
|
2
2
|
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
3
|
import {radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
4
5
|
|
|
5
6
|
export const styles = css.create({
|
|
6
7
|
root: {
|
|
@@ -26,8 +27,8 @@ export const styles = css.create({
|
|
|
26
27
|
touchAction: 'none',
|
|
27
28
|
userSelect: 'none',
|
|
28
29
|
transitionProperty: 'opacity',
|
|
29
|
-
transitionDuration:
|
|
30
|
-
transitionTimingFunction:
|
|
30
|
+
transitionDuration: duration.base,
|
|
31
|
+
transitionTimingFunction: easing.standard,
|
|
31
32
|
},
|
|
32
33
|
scrollbarVertical: {
|
|
33
34
|
top: 0,
|
|
@@ -64,7 +65,7 @@ export const styles = css.create({
|
|
|
64
65
|
},
|
|
65
66
|
borderRadius: radii.full,
|
|
66
67
|
transitionProperty: 'background-color',
|
|
67
|
-
transitionDuration:
|
|
68
|
+
transitionDuration: duration.fast,
|
|
68
69
|
},
|
|
69
70
|
// Dynamic styles — simple identifier params only (StyleX constraint)
|
|
70
71
|
viewportMaxHeight: (maxHeight: number | string) => ({
|
|
@@ -3,6 +3,7 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
|
3
3
|
import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
|
+
import {duration} from '@duro-app/tokens/tokens/motion.css'
|
|
6
7
|
|
|
7
8
|
export const styles = css.create({
|
|
8
9
|
root: {
|
|
@@ -31,7 +32,7 @@ export const styles = css.create({
|
|
|
31
32
|
borderRadius: radii.sm,
|
|
32
33
|
cursor: 'pointer',
|
|
33
34
|
transitionProperty: 'border-color',
|
|
34
|
-
transitionDuration:
|
|
35
|
+
transitionDuration: duration.fast,
|
|
35
36
|
},
|
|
36
37
|
value: {
|
|
37
38
|
color: colors.text,
|
|
@@ -94,7 +95,7 @@ export const styles = css.create({
|
|
|
94
95
|
cursor: 'pointer',
|
|
95
96
|
backgroundColor: 'transparent',
|
|
96
97
|
transitionProperty: 'background-color',
|
|
97
|
-
transitionDuration:
|
|
98
|
+
transitionDuration: duration.fast,
|
|
98
99
|
},
|
|
99
100
|
itemSelected: {
|
|
100
101
|
color: colors.accent,
|
|
@@ -2,27 +2,33 @@ import type {ComponentMeta} from '../component-meta'
|
|
|
2
2
|
|
|
3
3
|
export const meta: ComponentMeta = {
|
|
4
4
|
description:
|
|
5
|
-
'Vertical side navigation
|
|
6
|
-
whenToUse: [
|
|
5
|
+
'Vertical side navigation. Use collapsible `Group`s (chevron accordion) or static `Section`s (always-open headers) — or a mix. `Item`s take an optional `icon` and show an active left-marker. Supports controlled and uncontrolled active item. Compound component — Root is required.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'App-level sidebar navigation',
|
|
8
|
+
'Settings navigation with grouped sections',
|
|
9
|
+
'Flat menu with labelled, always-open regions — use Section instead of Group',
|
|
10
|
+
],
|
|
7
11
|
whenNotToUse: [
|
|
8
12
|
'In-page content switching — use Tabs',
|
|
9
13
|
'Top-level horizontal nav — use Inline with LinkButton',
|
|
10
14
|
],
|
|
11
15
|
anatomy: {
|
|
12
|
-
required: ['Root', '
|
|
16
|
+
required: ['Root', 'Item'],
|
|
17
|
+
optional: ['Group', 'Section'],
|
|
13
18
|
},
|
|
14
19
|
relatedTo: [
|
|
15
20
|
{component: 'Tabs', relationship: 'Tabs switch content in-place; SideNav navigates pages'},
|
|
16
21
|
{component: 'PageShell', relationship: 'Often placed alongside PageShell'},
|
|
17
22
|
],
|
|
18
|
-
example: `<SideNav.Root defaultValue="
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<SideNav.Item value="
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
example: `<SideNav.Root defaultValue="identities">
|
|
24
|
+
{/* Static, always-open sections with icons (flat menu) */}
|
|
25
|
+
<SideNav.Section label="People & access">
|
|
26
|
+
<SideNav.Item value="identities" icon={<Icon name="users" size={18} />}>Identities</SideNav.Item>
|
|
27
|
+
<SideNav.Item value="grants" icon={<Icon name="key" size={18} />}>Grants</SideNav.Item>
|
|
28
|
+
</SideNav.Section>
|
|
29
|
+
{/* Or collapsible groups (accordion) */}
|
|
30
|
+
<SideNav.Group label="Advanced" defaultExpanded={false}>
|
|
31
|
+
<SideNav.Item value="plugins" icon={<Icon name="plug" size={18} />}>Plugins</SideNav.Item>
|
|
26
32
|
</SideNav.Group>
|
|
27
33
|
</SideNav.Root>`,
|
|
28
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {Meta, StoryObj} from '@storybook/react'
|
|
2
2
|
import {expect, fn} from 'storybook/test'
|
|
3
3
|
import {SideNav} from './SideNav'
|
|
4
|
+
import {Icon} from '../Icon/Icon'
|
|
4
5
|
|
|
5
6
|
const meta: Meta<typeof SideNav.Root> = {
|
|
6
7
|
title: 'Components/SideNav',
|
|
@@ -31,7 +32,7 @@ export const Default: Story = {
|
|
|
31
32
|
const nav = canvas.getByRole('navigation')
|
|
32
33
|
await expect(nav).toBeInTheDocument()
|
|
33
34
|
|
|
34
|
-
const activeItem = canvas.
|
|
35
|
+
const activeItem = canvas.getByRole('button', {name: 'Dashboard'})
|
|
35
36
|
await expect(activeItem).toHaveAttribute('aria-current', 'page')
|
|
36
37
|
},
|
|
37
38
|
}
|
|
@@ -74,7 +75,7 @@ export const DefaultExpandedWithActiveItem: Story = {
|
|
|
74
75
|
play: async ({canvas}) => {
|
|
75
76
|
const trigger = canvas.getByText('Settings')
|
|
76
77
|
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
77
|
-
const active = canvas.
|
|
78
|
+
const active = canvas.getByRole('button', {name: 'Security'})
|
|
78
79
|
await expect(active).toBeInTheDocument()
|
|
79
80
|
await expect(active).toHaveAttribute('aria-current', 'page')
|
|
80
81
|
},
|
|
@@ -91,9 +92,52 @@ export const Interactive: Story = {
|
|
|
91
92
|
</SideNav.Root>
|
|
92
93
|
),
|
|
93
94
|
play: async ({canvas, userEvent, args}) => {
|
|
94
|
-
const aboutItem = canvas.
|
|
95
|
+
const aboutItem = canvas.getByRole('button', {name: 'About'})
|
|
95
96
|
await userEvent.click(aboutItem)
|
|
96
97
|
await expect(args.onValueChange).toHaveBeenCalledWith('about')
|
|
97
98
|
await expect(aboutItem).toHaveAttribute('aria-current', 'page')
|
|
98
99
|
},
|
|
99
100
|
}
|
|
101
|
+
|
|
102
|
+
// Flat menu: static (non-collapsible) Section headers + per-item icons + the
|
|
103
|
+
// active left-marker. This is the AppChrome-style layout — no chevrons, every
|
|
104
|
+
// section always open.
|
|
105
|
+
export const FlatWithIcons: Story = {
|
|
106
|
+
render: (args) => (
|
|
107
|
+
<SideNav.Root {...args} defaultValue="grants">
|
|
108
|
+
<SideNav.Section label="People & access">
|
|
109
|
+
<SideNav.Item value="identities" icon={<Icon name="users" size={18} />}>
|
|
110
|
+
Identities
|
|
111
|
+
</SideNav.Item>
|
|
112
|
+
<SideNav.Item value="grants" icon={<Icon name="key" size={18} />}>
|
|
113
|
+
Grants
|
|
114
|
+
</SideNav.Item>
|
|
115
|
+
</SideNav.Section>
|
|
116
|
+
<SideNav.Section label="Requests & invites">
|
|
117
|
+
<SideNav.Item value="invitations" icon={<Icon name="mail" size={18} />}>
|
|
118
|
+
Invitations
|
|
119
|
+
</SideNav.Item>
|
|
120
|
+
<SideNav.Item value="invites" icon={<Icon name="user-plus" size={18} />}>
|
|
121
|
+
User Invites
|
|
122
|
+
</SideNav.Item>
|
|
123
|
+
</SideNav.Section>
|
|
124
|
+
</SideNav.Root>
|
|
125
|
+
),
|
|
126
|
+
play: async ({canvas, userEvent, args}) => {
|
|
127
|
+
// Static section headers render as plain text (no toggle button).
|
|
128
|
+
await expect(canvas.getByText('People & access')).toBeInTheDocument()
|
|
129
|
+
await expect(canvas.queryByRole('button', {name: 'People & access'})).toBeNull()
|
|
130
|
+
|
|
131
|
+
// All items are visible immediately (nothing is collapsed).
|
|
132
|
+
const grants = canvas.getByRole('button', {name: 'Grants'})
|
|
133
|
+
await expect(grants).toHaveAttribute('aria-current', 'page')
|
|
134
|
+
await expect(canvas.getByRole('button', {name: 'Identities'})).toBeInTheDocument()
|
|
135
|
+
await expect(canvas.getByRole('button', {name: 'User Invites'})).toBeInTheDocument()
|
|
136
|
+
|
|
137
|
+
// Selecting another item moves the active state.
|
|
138
|
+
const identities = canvas.getByRole('button', {name: 'Identities'})
|
|
139
|
+
await userEvent.click(identities)
|
|
140
|
+
await expect(args.onValueChange).toHaveBeenCalledWith('identities')
|
|
141
|
+
await expect(identities).toHaveAttribute('aria-current', 'page')
|
|
142
|
+
},
|
|
143
|
+
}
|
|
@@ -150,14 +150,39 @@ function Group({children, label, groupKey, defaultExpanded}: GroupProps) {
|
|
|
150
150
|
)
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// --- Section ---
|
|
154
|
+
|
|
155
|
+
interface SectionProps {
|
|
156
|
+
children: ReactNode
|
|
157
|
+
label: string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A static, non-collapsible grouping: an always-visible uppercase header with
|
|
162
|
+
* its items below. Unlike `Group` there is no chevron, toggle, or hidden state
|
|
163
|
+
* — use it when every section should stay open at a glance (a flat menu with
|
|
164
|
+
* labelled regions rather than an accordion).
|
|
165
|
+
*/
|
|
166
|
+
function Section({children, label}: SectionProps) {
|
|
167
|
+
return (
|
|
168
|
+
<html.div style={styles.section}>
|
|
169
|
+
<html.div style={styles.sectionLabel}>{label}</html.div>
|
|
170
|
+
{children}
|
|
171
|
+
</html.div>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
153
175
|
// --- Item ---
|
|
154
176
|
|
|
155
177
|
interface ItemProps {
|
|
156
178
|
value: string
|
|
157
179
|
children: ReactNode
|
|
180
|
+
/** Optional leading glyph (e.g. a design-system `<Icon />`). Inherits the
|
|
181
|
+
* item's text color, so it turns accent when the item is active. */
|
|
182
|
+
icon?: ReactNode
|
|
158
183
|
}
|
|
159
184
|
|
|
160
|
-
function Item({value, children}: ItemProps) {
|
|
185
|
+
function Item({value, children, icon}: ItemProps) {
|
|
161
186
|
const {activeValue, onSelect, registerItem} = useSideNav()
|
|
162
187
|
const isActive = activeValue === value
|
|
163
188
|
|
|
@@ -173,7 +198,13 @@ function Item({value, children}: ItemProps) {
|
|
|
173
198
|
style={[styles.item, isActive && styles.itemActive]}
|
|
174
199
|
aria-current={isActive ? 'page' : undefined}
|
|
175
200
|
>
|
|
176
|
-
{
|
|
201
|
+
<html.span aria-hidden style={[styles.marker, isActive && styles.markerActive]} />
|
|
202
|
+
{icon ? (
|
|
203
|
+
<html.span aria-hidden style={styles.itemIcon}>
|
|
204
|
+
{icon}
|
|
205
|
+
</html.span>
|
|
206
|
+
) : null}
|
|
207
|
+
<html.span style={styles.itemLabel}>{children}</html.span>
|
|
177
208
|
</html.button>
|
|
178
209
|
)
|
|
179
210
|
}
|
|
@@ -181,5 +212,6 @@ function Item({value, children}: ItemProps) {
|
|
|
181
212
|
export const SideNav = {
|
|
182
213
|
Root,
|
|
183
214
|
Group,
|
|
215
|
+
Section,
|
|
184
216
|
Item,
|
|
185
217
|
}
|