@duro-app/ui 5.11.0 → 5.12.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/Table-ColfpgKV.js.map +1 -1
- package/dist/compiler-runtime-BuugS4D0.js.map +1 -1
- package/dist/components/Alert/Alert.d.ts.map +1 -1
- package/dist/components/Button/Button.d.ts +2 -1
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/styles.css.d.ts +1 -0
- package/dist/components/Button/styles.css.d.ts.map +1 -1
- package/dist/components/Callout/Callout.d.ts.map +1 -1
- package/dist/components/Callout/styles.css.d.ts +3 -0
- package/dist/components/Callout/styles.css.d.ts.map +1 -1
- package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -1
- package/dist/components/DetailPanel/DetailPanel.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.d.ts +2 -17
- package/dist/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.stories.d.ts +4 -0
- package/dist/components/Dialog/Dialog.stories.d.ts.map +1 -1
- package/dist/components/Dialog/native.css.d.ts +82 -0
- package/dist/components/Dialog/native.css.d.ts.map +1 -0
- package/dist/components/Dialog/parts.css.d.ts +82 -0
- package/dist/components/Dialog/parts.css.d.ts.map +1 -0
- package/dist/components/Dialog/parts.d.ts +35 -0
- package/dist/components/Dialog/parts.d.ts.map +1 -0
- package/dist/components/Dialog/styles.css.d.ts +1 -74
- package/dist/components/Dialog/styles.css.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts.map +1 -1
- package/dist/components/Drawer/styles.css.d.ts +2 -0
- package/dist/components/Drawer/styles.css.d.ts.map +1 -1
- package/dist/components/Grid/styles.css.d.ts +1 -0
- package/dist/components/Grid/styles.css.d.ts.map +1 -1
- package/dist/components/Icon/Icon.d.ts +3 -9
- package/dist/components/Icon/Icon.d.ts.map +1 -1
- package/dist/components/Icon/geometry.d.ts +60 -0
- package/dist/components/Icon/geometry.d.ts.map +1 -0
- package/dist/components/Icon/types.d.ts +15 -0
- package/dist/components/Icon/types.d.ts.map +1 -0
- package/dist/components/Input/Input.d.ts +5 -1
- package/dist/components/Input/Input.d.ts.map +1 -1
- package/dist/components/Input/styles.css.d.ts +1 -0
- package/dist/components/Input/styles.css.d.ts.map +1 -1
- package/dist/components/LinkButton/styles.css.d.ts +1 -0
- package/dist/components/LinkButton/styles.css.d.ts.map +1 -1
- package/dist/components/Meter/styles.css.d.ts +1 -0
- package/dist/components/Meter/styles.css.d.ts.map +1 -1
- package/dist/components/PageShell/styles.css.d.ts +2 -0
- package/dist/components/PageShell/styles.css.d.ts.map +1 -1
- package/dist/components/RadioGroup/RadioGroup.d.ts +1 -17
- package/dist/components/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/dist/components/RadioGroup/RadioGroup.stories.d.ts +3 -0
- package/dist/components/RadioGroup/RadioGroup.stories.d.ts.map +1 -1
- package/dist/components/RadioGroup/group.d.ts +31 -0
- package/dist/components/RadioGroup/group.d.ts.map +1 -0
- package/dist/components/RadioGroup/styles.css.d.ts +10 -0
- package/dist/components/RadioGroup/styles.css.d.ts.map +1 -1
- package/dist/components/ScrollArea/styles.css.d.ts +1 -0
- package/dist/components/ScrollArea/styles.css.d.ts.map +1 -1
- package/dist/components/Spinner/Spinner.d.ts +2 -6
- package/dist/components/Spinner/Spinner.d.ts.map +1 -1
- package/dist/components/Spinner/Spinner.types.d.ts +6 -0
- package/dist/components/Spinner/Spinner.types.d.ts.map +1 -0
- package/dist/components/Spinner/ring.css.d.ts +39 -0
- package/dist/components/Spinner/ring.css.d.ts.map +1 -0
- package/dist/components/Spinner/styles.css.d.ts +1 -28
- package/dist/components/Spinner/styles.css.d.ts.map +1 -1
- package/dist/components/StatusIcon/StatusIcon.d.ts.map +1 -1
- package/dist/components/StatusIcon/styles.css.d.ts +3 -0
- package/dist/components/StatusIcon/styles.css.d.ts.map +1 -1
- package/dist/components/Switch/Switch.d.ts +2 -18
- package/dist/components/Switch/Switch.d.ts.map +1 -1
- package/dist/components/Switch/Switch.types.d.ts +18 -0
- package/dist/components/Switch/Switch.types.d.ts.map +1 -0
- package/dist/components/TableOfContents/styles.css.d.ts +1 -0
- package/dist/components/TableOfContents/styles.css.d.ts.map +1 -1
- package/dist/components/Textarea/styles.css.d.ts +1 -0
- package/dist/components/Textarea/styles.css.d.ts.map +1 -1
- package/dist/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle/styles.css.d.ts +11 -2
- package/dist/components/Toggle/styles.css.d.ts.map +1 -1
- package/dist/components/VirtualTable/styles.css.d.ts +6 -0
- package/dist/components/VirtualTable/styles.css.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +4299 -3566
- package/dist/index.js.map +1 -1
- package/dist/shared/portal/hostRegistry.d.ts +33 -0
- package/dist/shared/portal/hostRegistry.d.ts.map +1 -0
- package/dist/shared/useOverlayOpenState.d.ts +40 -0
- package/dist/shared/useOverlayOpenState.d.ts.map +1 -0
- package/dist/table.js +141 -135
- package/dist/table.js.map +1 -1
- package/package.json +34 -4
- package/src/components/Alert/Alert.tsx +14 -2
- package/src/components/Button/Button.tsx +3 -2
- package/src/components/Button/styles.css.ts +2 -0
- package/src/components/Callout/Callout.tsx +17 -3
- package/src/components/Callout/styles.css.ts +5 -0
- package/src/components/Checkbox/Checkbox.native.tsx +67 -0
- package/src/components/Checkbox/Checkbox.tsx +1 -50
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +3 -0
- package/src/components/DetailPanel/DetailPanel.tsx +9 -45
- package/src/components/Dialog/Dialog.native.tsx +296 -0
- package/src/components/Dialog/Dialog.stories.tsx +71 -0
- package/src/components/Dialog/Dialog.tsx +41 -130
- package/src/components/Dialog/native.css.ts +78 -0
- package/src/components/Dialog/parts.css.ts +101 -0
- package/src/components/Dialog/parts.tsx +61 -0
- package/src/components/Dialog/styles.css.ts +2 -89
- package/src/components/Drawer/Drawer.tsx +20 -49
- package/src/components/Drawer/styles.css.ts +4 -0
- package/src/components/Grid/styles.css.ts +2 -0
- package/src/components/Icon/Icon.native.tsx +54 -0
- package/src/components/Icon/Icon.tsx +19 -430
- package/src/components/Icon/geometry.ts +456 -0
- package/src/components/Icon/types.ts +74 -0
- package/src/components/Input/Input.tsx +8 -0
- package/src/components/Input/styles.css.ts +2 -0
- package/src/components/LinkButton/styles.css.ts +2 -0
- package/src/components/Meter/styles.css.ts +2 -0
- package/src/components/PageShell/styles.css.ts +4 -0
- package/src/components/RadioGroup/RadioGroup.meta.ts +1 -1
- package/src/components/RadioGroup/RadioGroup.native.tsx +48 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +64 -1
- package/src/components/RadioGroup/RadioGroup.tsx +8 -83
- package/src/components/RadioGroup/group.tsx +85 -0
- package/src/components/RadioGroup/styles.css.ts +13 -0
- package/src/components/ScrollArea/styles.css.ts +2 -0
- package/src/components/Spinner/Spinner.native.tsx +37 -0
- package/src/components/Spinner/Spinner.tsx +6 -15
- package/src/components/Spinner/Spinner.types.ts +6 -0
- package/src/components/Spinner/ring.css.ts +45 -0
- package/src/components/Spinner/styles.css.ts +2 -32
- package/src/components/StatusIcon/StatusIcon.tsx +13 -2
- package/src/components/StatusIcon/styles.css.ts +5 -0
- package/src/components/Switch/Switch.native.tsx +45 -0
- package/src/components/Switch/Switch.tsx +22 -44
- package/src/components/Switch/Switch.types.ts +18 -0
- package/src/components/TableOfContents/styles.css.ts +2 -0
- package/src/components/Textarea/styles.css.ts +2 -0
- package/src/components/ThemeProvider/ThemeProvider.native.tsx +70 -10
- package/src/components/Toggle/Toggle.tsx +2 -0
- package/src/components/Toggle/styles.css.ts +14 -2
- package/src/components/VirtualTable/styles.css.ts +12 -0
- package/src/index.native.ts +28 -1
- package/src/native-guard/globals.d.ts +10 -0
- package/src/native-guard/react-dom.d.ts +3 -0
- package/src/shared/accessibilityFocus.native.ts +23 -0
- package/src/shared/portal/OverlayModal.native.tsx +51 -0
- package/src/shared/portal/PortalHost.native.tsx +102 -0
- package/src/shared/portal/hostRegistry.ts +76 -0
- package/src/shared/portal/themeScope.native.ts +18 -0
- package/src/shared/safeArea.native.ts +22 -0
- package/src/shared/useOverlayOpenState.ts +128 -0
- package/dist/components/ThemeProvider/ThemeProvider.native.d.ts +0 -10
- package/dist/components/ThemeProvider/ThemeProvider.native.d.ts.map +0 -1
- package/dist/index.native.d.ts +0 -33
- package/dist/index.native.d.ts.map +0 -1
- package/dist/platform.native.d.ts +0 -2
- package/dist/platform.native.d.ts.map +0 -1
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
useRef,
|
|
12
12
|
} from 'react'
|
|
13
13
|
import {html} from 'react-strict-dom'
|
|
14
|
-
import
|
|
14
|
+
import type {DurationToken} from '@duro-app/tokens/keys'
|
|
15
15
|
import {styles} from './styles.css'
|
|
16
|
+
import {useOverlayOpenState} from '../../shared/useOverlayOpenState'
|
|
16
17
|
import {usePortalMount} from '../ThemeProvider/ThemeProvider'
|
|
17
18
|
import {ControlContextBoundary} from '../Toolbar/ControlContextBoundary'
|
|
18
19
|
import {ModalContext} from '../../shared/ModalContext'
|
|
@@ -89,57 +90,27 @@ function Root({
|
|
|
89
90
|
swipeDismiss = true,
|
|
90
91
|
closeAnimationDuration = 'fast',
|
|
91
92
|
}: RootProps) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
const {
|
|
94
|
+
present,
|
|
95
|
+
closing,
|
|
96
|
+
requestOpen,
|
|
97
|
+
requestClose,
|
|
98
|
+
closeNow: requestCloseImmediate,
|
|
99
|
+
} = useOverlayOpenState({
|
|
100
|
+
open: controlledOpen,
|
|
101
|
+
defaultOpen,
|
|
102
|
+
onOpenChange,
|
|
103
|
+
exitDuration: closeAnimationDuration,
|
|
104
|
+
closeNotice: 'after-exit',
|
|
105
|
+
})
|
|
96
106
|
const panelRef = useRef<HTMLDivElement>(null)
|
|
97
107
|
|
|
98
|
-
const isOpen = isControlled ? controlledOpen : internalOpen
|
|
99
|
-
|
|
100
108
|
const titleId = useId()
|
|
101
109
|
const descriptionId = useId()
|
|
102
110
|
|
|
103
|
-
const requestOpen = useCallback(() => {
|
|
104
|
-
if (closingTimerRef.current) {
|
|
105
|
-
clearTimeout(closingTimerRef.current)
|
|
106
|
-
closingTimerRef.current = null
|
|
107
|
-
}
|
|
108
|
-
setClosing(false)
|
|
109
|
-
if (!isControlled) setInternalOpen(true)
|
|
110
|
-
onOpenChange?.(true)
|
|
111
|
-
}, [isControlled, onOpenChange])
|
|
112
|
-
|
|
113
|
-
const requestClose = useCallback(() => {
|
|
114
|
-
setClosing(true)
|
|
115
|
-
closingTimerRef.current = setTimeout(() => {
|
|
116
|
-
setClosing(false)
|
|
117
|
-
closingTimerRef.current = null
|
|
118
|
-
if (!isControlled) setInternalOpen(false)
|
|
119
|
-
onOpenChange?.(false)
|
|
120
|
-
}, DURATION_MS[closeAnimationDuration])
|
|
121
|
-
}, [isControlled, onOpenChange, closeAnimationDuration])
|
|
122
|
-
|
|
123
|
-
const requestCloseImmediate = useCallback(() => {
|
|
124
|
-
if (closingTimerRef.current) {
|
|
125
|
-
clearTimeout(closingTimerRef.current)
|
|
126
|
-
closingTimerRef.current = null
|
|
127
|
-
}
|
|
128
|
-
setClosing(false)
|
|
129
|
-
if (!isControlled) setInternalOpen(false)
|
|
130
|
-
onOpenChange?.(false)
|
|
131
|
-
}, [isControlled, onOpenChange])
|
|
132
|
-
|
|
133
|
-
// Clean up timer on unmount
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
return () => {
|
|
136
|
-
if (closingTimerRef.current) clearTimeout(closingTimerRef.current)
|
|
137
|
-
}
|
|
138
|
-
}, [])
|
|
139
|
-
|
|
140
111
|
// Handle Escape key
|
|
141
112
|
useEffect(() => {
|
|
142
|
-
if (!
|
|
113
|
+
if (!present) return
|
|
143
114
|
if (!dismissable) return
|
|
144
115
|
|
|
145
116
|
function onKeyDown(e: KeyboardEvent) {
|
|
@@ -150,23 +121,23 @@ function Root({
|
|
|
150
121
|
}
|
|
151
122
|
document.addEventListener('keydown', onKeyDown)
|
|
152
123
|
return () => document.removeEventListener('keydown', onKeyDown)
|
|
153
|
-
}, [
|
|
124
|
+
}, [present, dismissable, requestClose])
|
|
154
125
|
|
|
155
126
|
// Lock body scroll when open
|
|
156
127
|
useEffect(() => {
|
|
157
|
-
if (
|
|
128
|
+
if (present) {
|
|
158
129
|
const prev = document.body.style.overflow
|
|
159
130
|
document.body.style.overflow = 'hidden'
|
|
160
131
|
return () => {
|
|
161
132
|
document.body.style.overflow = prev
|
|
162
133
|
}
|
|
163
134
|
}
|
|
164
|
-
}, [
|
|
135
|
+
}, [present])
|
|
165
136
|
|
|
166
137
|
return (
|
|
167
138
|
<DrawerContext.Provider
|
|
168
139
|
value={{
|
|
169
|
-
open:
|
|
140
|
+
open: present,
|
|
170
141
|
closing,
|
|
171
142
|
closeDuration: closeAnimationDuration,
|
|
172
143
|
anchor,
|
|
@@ -79,6 +79,8 @@ export const styles = css.create({
|
|
|
79
79
|
// Panel sizing per anchor
|
|
80
80
|
panelHorizontal: {
|
|
81
81
|
width: '100%',
|
|
82
|
+
// Native has no border-box reset (the web's global-reset.css); declare it.
|
|
83
|
+
boxSizing: 'border-box',
|
|
82
84
|
height: '100%',
|
|
83
85
|
borderLeftWidth: borders.hairline,
|
|
84
86
|
borderLeftStyle: 'solid',
|
|
@@ -86,6 +88,8 @@ export const styles = css.create({
|
|
|
86
88
|
},
|
|
87
89
|
panelVertical: {
|
|
88
90
|
width: '100%',
|
|
91
|
+
// Native has no border-box reset (the web's global-reset.css); declare it.
|
|
92
|
+
boxSizing: 'border-box',
|
|
89
93
|
borderTopWidth: borders.hairline,
|
|
90
94
|
borderTopStyle: 'solid',
|
|
91
95
|
borderTopColor: colors.border,
|
|
@@ -86,6 +86,8 @@ export const styles = css.create({
|
|
|
86
86
|
// against an auto width the line breaks come out wrong and the container
|
|
87
87
|
// under-reports its height (verified on the iOS simulator).
|
|
88
88
|
width: '100%',
|
|
89
|
+
// Native has no border-box reset (the web's global-reset.css); declare it.
|
|
90
|
+
boxSizing: 'border-box',
|
|
89
91
|
},
|
|
90
92
|
// border-box is explicit: react-strict-dom mirrors the web default
|
|
91
93
|
// (content-box) on native, which adds the gap padding on top of the basis
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {createElement} from 'react'
|
|
2
|
+
import Svg, {Circle, Ellipse, Line, Path, Polygon, Polyline, Rect} from 'react-native-svg'
|
|
3
|
+
import {ICON_SIZES, type ColorToken} from '@duro-app/tokens/keys'
|
|
4
|
+
import {useResolvedColors} from '../ThemeProvider/ThemeProvider'
|
|
5
|
+
import {GLYPHS, type Paint, type Shape} from './geometry'
|
|
6
|
+
import type {IconProps} from './types'
|
|
7
|
+
|
|
8
|
+
export type {IconName, IconProps, IconSize} from './types'
|
|
9
|
+
|
|
10
|
+
const elements = {
|
|
11
|
+
path: Path,
|
|
12
|
+
circle: Circle,
|
|
13
|
+
ellipse: Ellipse,
|
|
14
|
+
line: Line,
|
|
15
|
+
rect: Rect,
|
|
16
|
+
polyline: Polyline,
|
|
17
|
+
polygon: Polygon,
|
|
18
|
+
} as const
|
|
19
|
+
|
|
20
|
+
/** react-native-svg reads `currentColor` from the Svg's `color`, which is a resolved colour here. */
|
|
21
|
+
function nativeShape(shape: Shape, key: number) {
|
|
22
|
+
const {tag, ...attrs} = shape
|
|
23
|
+
return createElement(elements[tag] as never, {key, ...attrs})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Native Icon: the same geometry as the web, drawn with react-native-svg
|
|
28
|
+
* (plan N0, Decision 9; RSD has no `svg`). It cannot inherit a text colour,
|
|
29
|
+
* so its colour is a token resolved through the nearest ThemeProvider. It is
|
|
30
|
+
* decorative: the control that hosts it carries the label.
|
|
31
|
+
*/
|
|
32
|
+
export function Icon({name, size: sizeToken = 'lg', color = 'text'}: IconProps) {
|
|
33
|
+
const size = ICON_SIZES[sizeToken]
|
|
34
|
+
const glyph = GLYPHS[name]
|
|
35
|
+
const resolved = useResolvedColors()[color as ColorToken]
|
|
36
|
+
const paint: {fill: Paint; stroke?: Paint} =
|
|
37
|
+
glyph.style === 'fill' ? {fill: 'currentColor'} : {fill: 'none', stroke: 'currentColor'}
|
|
38
|
+
return (
|
|
39
|
+
<Svg
|
|
40
|
+
viewBox="0 0 24 24"
|
|
41
|
+
width={size}
|
|
42
|
+
height={size}
|
|
43
|
+
color={resolved}
|
|
44
|
+
{...paint}
|
|
45
|
+
strokeWidth={glyph.style === 'stroke' ? 2 : undefined}
|
|
46
|
+
strokeLinecap="round"
|
|
47
|
+
strokeLinejoin="round"
|
|
48
|
+
accessibilityElementsHidden
|
|
49
|
+
importantForAccessibility="no-hide-descendants"
|
|
50
|
+
>
|
|
51
|
+
{glyph.shapes.map(nativeShape)}
|
|
52
|
+
</Svg>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -1,439 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {ICON_SIZES
|
|
1
|
+
import {createElement} from 'react'
|
|
2
|
+
import {ICON_SIZES} from '@duro-app/tokens/keys'
|
|
3
|
+
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
import {GLYPHS, type Shape} from './geometry'
|
|
6
|
+
import type {IconProps} from './types'
|
|
5
7
|
|
|
6
|
-
export type IconName
|
|
7
|
-
| 'x-circle'
|
|
8
|
-
| 'check-circle'
|
|
9
|
-
| 'check-done'
|
|
10
|
-
| 'clock'
|
|
11
|
-
| 'forbidden'
|
|
12
|
-
| 'info-circle'
|
|
13
|
-
| 'alert-triangle'
|
|
14
|
-
| 'shield'
|
|
15
|
-
| 'lock'
|
|
16
|
-
| 'key'
|
|
17
|
-
// Navigation / wayfinding glyphs
|
|
18
|
-
| 'map'
|
|
19
|
-
| 'layers'
|
|
20
|
-
| 'repeat'
|
|
21
|
-
| 'database'
|
|
22
|
-
| 'shield-check'
|
|
23
|
-
| 'route'
|
|
24
|
-
| 'git-branch'
|
|
25
|
-
| 'menu'
|
|
26
|
-
| 'pin'
|
|
27
|
-
// Infrastructure / inventory glyphs
|
|
28
|
-
| 'server'
|
|
29
|
-
| 'hard-drive'
|
|
30
|
-
| 'box'
|
|
31
|
-
| 'image'
|
|
32
|
-
| 'tag'
|
|
33
|
-
| 'pie-chart'
|
|
34
|
-
// People / access / admin glyphs
|
|
35
|
-
| 'users'
|
|
36
|
-
| 'user-plus'
|
|
37
|
-
| 'mail'
|
|
38
|
-
| 'file-text'
|
|
39
|
-
| 'plug'
|
|
40
|
-
// Input / action glyphs
|
|
41
|
-
| 'search'
|
|
42
|
-
| 'mic'
|
|
43
|
-
// Color-mode glyphs
|
|
44
|
-
| 'sun'
|
|
45
|
-
| 'moon'
|
|
46
|
-
| 'monitor'
|
|
47
|
-
| 'contrast'
|
|
48
|
-
// Device status glyphs
|
|
49
|
-
| 'signal'
|
|
50
|
-
| 'battery'
|
|
51
|
-
// Media glyphs
|
|
52
|
-
| 'video'
|
|
53
|
-
| 'music'
|
|
54
|
-
| 'play'
|
|
55
|
-
| 'pause'
|
|
56
|
-
// Filled variants (solid shape with cutout symbol)
|
|
57
|
-
| 'info-circle-filled'
|
|
58
|
-
| 'alert-triangle-filled'
|
|
59
|
-
| 'check-circle-filled'
|
|
60
|
-
| 'x-circle-filled'
|
|
61
|
-
| 'shield-filled'
|
|
62
|
-
| 'lock-filled'
|
|
8
|
+
export type {IconName, IconProps, IconSize} from './types'
|
|
63
9
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<circle cx="12" cy="12" r="10" />
|
|
69
|
-
<line x1="15" y1="9" x2="9" y2="15" />
|
|
70
|
-
<line x1="9" y1="9" x2="15" y2="15" />
|
|
71
|
-
</>
|
|
72
|
-
),
|
|
73
|
-
'check-circle': (
|
|
74
|
-
<>
|
|
75
|
-
<circle cx="12" cy="12" r="10" />
|
|
76
|
-
<polyline points="16 10 11 15 8 12" />
|
|
77
|
-
</>
|
|
78
|
-
),
|
|
79
|
-
'check-done': (
|
|
80
|
-
<>
|
|
81
|
-
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
82
|
-
<polyline points="22 4 12 14.01 9 11.01" />
|
|
83
|
-
</>
|
|
84
|
-
),
|
|
85
|
-
clock: (
|
|
86
|
-
<>
|
|
87
|
-
<circle cx="12" cy="12" r="10" />
|
|
88
|
-
<polyline points="12 6 12 12 16 14" />
|
|
89
|
-
</>
|
|
90
|
-
),
|
|
91
|
-
forbidden: (
|
|
92
|
-
<>
|
|
93
|
-
<circle cx="12" cy="12" r="10" />
|
|
94
|
-
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
|
|
95
|
-
</>
|
|
96
|
-
),
|
|
97
|
-
'info-circle': (
|
|
98
|
-
<>
|
|
99
|
-
<circle cx="12" cy="12" r="10" />
|
|
100
|
-
<line x1="12" y1="16" x2="12" y2="12" />
|
|
101
|
-
<line x1="12" y1="8" x2="12.01" y2="8" />
|
|
102
|
-
</>
|
|
103
|
-
),
|
|
104
|
-
'alert-triangle': (
|
|
105
|
-
<>
|
|
106
|
-
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
|
107
|
-
<line x1="12" y1="9" x2="12" y2="13" />
|
|
108
|
-
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
109
|
-
</>
|
|
110
|
-
),
|
|
111
|
-
shield: <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />,
|
|
112
|
-
lock: (
|
|
113
|
-
<>
|
|
114
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
|
|
115
|
-
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
|
116
|
-
</>
|
|
117
|
-
),
|
|
118
|
-
key: (
|
|
119
|
-
<>
|
|
120
|
-
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.78 7.78 5.5 5.5 0 0 1 7.78-7.78zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" />
|
|
121
|
-
</>
|
|
122
|
-
),
|
|
123
|
-
// ---- navigation / wayfinding ----
|
|
124
|
-
map: (
|
|
125
|
-
<>
|
|
126
|
-
<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6" />
|
|
127
|
-
<line x1="8" y1="2" x2="8" y2="18" />
|
|
128
|
-
<line x1="16" y1="6" x2="16" y2="22" />
|
|
129
|
-
</>
|
|
130
|
-
),
|
|
131
|
-
layers: (
|
|
132
|
-
<>
|
|
133
|
-
<polygon points="12 2 2 7 12 12 22 7 12 2" />
|
|
134
|
-
<polyline points="2 17 12 22 22 17" />
|
|
135
|
-
<polyline points="2 12 12 17 22 12" />
|
|
136
|
-
</>
|
|
137
|
-
),
|
|
138
|
-
// Two crossing arrows — exchange / interface flows.
|
|
139
|
-
repeat: (
|
|
140
|
-
<>
|
|
141
|
-
<polyline points="17 1 21 5 17 9" />
|
|
142
|
-
<path d="M3 11V9a4 4 0 0 1 4-4h14" />
|
|
143
|
-
<polyline points="7 23 3 19 7 15" />
|
|
144
|
-
<path d="M21 13v2a4 4 0 0 1-4 4H3" />
|
|
145
|
-
</>
|
|
146
|
-
),
|
|
147
|
-
database: (
|
|
148
|
-
<>
|
|
149
|
-
<ellipse cx="12" cy="5" rx="9" ry="3" />
|
|
150
|
-
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
|
151
|
-
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
|
152
|
-
</>
|
|
153
|
-
),
|
|
154
|
-
'shield-check': (
|
|
155
|
-
<>
|
|
156
|
-
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
|
157
|
-
<polyline points="9 12 11 14 15 10" />
|
|
158
|
-
</>
|
|
159
|
-
),
|
|
160
|
-
// Milestone path — roadmap / itinerary.
|
|
161
|
-
route: (
|
|
162
|
-
<>
|
|
163
|
-
<circle cx="6" cy="19" r="3" />
|
|
164
|
-
<path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" />
|
|
165
|
-
<circle cx="18" cy="5" r="3" />
|
|
166
|
-
</>
|
|
167
|
-
),
|
|
168
|
-
'git-branch': (
|
|
169
|
-
<>
|
|
170
|
-
<line x1="6" y1="3" x2="6" y2="15" />
|
|
171
|
-
<circle cx="18" cy="6" r="3" />
|
|
172
|
-
<circle cx="6" cy="18" r="3" />
|
|
173
|
-
<path d="M18 9a9 9 0 0 1-9 9" />
|
|
174
|
-
</>
|
|
175
|
-
),
|
|
176
|
-
menu: (
|
|
177
|
-
<>
|
|
178
|
-
<line x1="3" y1="6" x2="21" y2="6" />
|
|
179
|
-
<line x1="3" y1="12" x2="21" y2="12" />
|
|
180
|
-
<line x1="3" y1="18" x2="21" y2="18" />
|
|
181
|
-
</>
|
|
182
|
-
),
|
|
183
|
-
// ---- infrastructure / inventory ----
|
|
184
|
-
// Two stacked rack units — hosts, nodes, machines.
|
|
185
|
-
server: (
|
|
186
|
-
<>
|
|
187
|
-
<rect x="2" y="2" width="20" height="8" rx="2" ry="2" />
|
|
188
|
-
<rect x="2" y="14" width="20" height="8" rx="2" ry="2" />
|
|
189
|
-
<line x1="6" y1="6" x2="6.01" y2="6" />
|
|
190
|
-
<line x1="6" y1="18" x2="6.01" y2="18" />
|
|
191
|
-
</>
|
|
192
|
-
),
|
|
193
|
-
// Disk enclosure — volumes, persistent storage.
|
|
194
|
-
'hard-drive': (
|
|
195
|
-
<>
|
|
196
|
-
<line x1="22" y1="12" x2="2" y2="12" />
|
|
197
|
-
<path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
|
|
198
|
-
<line x1="6" y1="16" x2="6.01" y2="16" />
|
|
199
|
-
<line x1="10" y1="16" x2="10.01" y2="16" />
|
|
200
|
-
</>
|
|
201
|
-
),
|
|
202
|
-
// Isometric crate — a deployable unit of work.
|
|
203
|
-
box: (
|
|
204
|
-
<>
|
|
205
|
-
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
|
206
|
-
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
|
|
207
|
-
<line x1="12" y1="22.08" x2="12" y2="12" />
|
|
208
|
-
</>
|
|
209
|
-
),
|
|
210
|
-
// Picture frame — container images, artefacts.
|
|
211
|
-
image: (
|
|
212
|
-
<>
|
|
213
|
-
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
|
214
|
-
<circle cx="8.5" cy="8.5" r="1.5" />
|
|
215
|
-
<polyline points="21 15 16 10 5 21" />
|
|
216
|
-
</>
|
|
217
|
-
),
|
|
218
|
-
// Luggage tag — labels, annotations, metadata.
|
|
219
|
-
tag: (
|
|
220
|
-
<>
|
|
221
|
-
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" />
|
|
222
|
-
<line x1="7" y1="7" x2="7.01" y2="7" />
|
|
223
|
-
</>
|
|
224
|
-
),
|
|
225
|
-
// Share of a whole — quotas, budgets, utilisation.
|
|
226
|
-
'pie-chart': (
|
|
227
|
-
<>
|
|
228
|
-
<path d="M21.21 15.89A10 10 0 1 1 8 2.83" />
|
|
229
|
-
<path d="M22 12A10 10 0 0 0 12 2v10z" />
|
|
230
|
-
</>
|
|
231
|
-
),
|
|
232
|
-
// ---- people / access / admin ----
|
|
233
|
-
users: (
|
|
234
|
-
<>
|
|
235
|
-
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
236
|
-
<circle cx="9" cy="7" r="4" />
|
|
237
|
-
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
|
238
|
-
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
239
|
-
</>
|
|
240
|
-
),
|
|
241
|
-
'user-plus': (
|
|
242
|
-
<>
|
|
243
|
-
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
244
|
-
<circle cx="8.5" cy="7" r="4" />
|
|
245
|
-
<line x1="20" y1="8" x2="20" y2="14" />
|
|
246
|
-
<line x1="23" y1="11" x2="17" y2="11" />
|
|
247
|
-
</>
|
|
248
|
-
),
|
|
249
|
-
mail: (
|
|
250
|
-
<>
|
|
251
|
-
<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" />
|
|
252
|
-
<polyline points="22,6 12,13 2,6" />
|
|
253
|
-
</>
|
|
254
|
-
),
|
|
255
|
-
'file-text': (
|
|
256
|
-
<>
|
|
257
|
-
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
258
|
-
<polyline points="14 2 14 8 20 8" />
|
|
259
|
-
<line x1="16" y1="13" x2="8" y2="13" />
|
|
260
|
-
<line x1="16" y1="17" x2="8" y2="17" />
|
|
261
|
-
<polyline points="10 9 9 9 8 9" />
|
|
262
|
-
</>
|
|
263
|
-
),
|
|
264
|
-
plug: (
|
|
265
|
-
<>
|
|
266
|
-
<path d="M12 22v-5" />
|
|
267
|
-
<path d="M9 8V2" />
|
|
268
|
-
<path d="M15 8V2" />
|
|
269
|
-
<path d="M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z" />
|
|
270
|
-
</>
|
|
271
|
-
),
|
|
272
|
-
pin: (
|
|
273
|
-
<>
|
|
274
|
-
<line x1="12" y1="17" x2="12" y2="22" />
|
|
275
|
-
<path d="M9 10.76V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v6.76a2 2 0 0 0 .55 1.38l1.68 1.78A1 1 0 0 1 16.5 16h-9a1 1 0 0 1-.73-1.68l1.68-1.78A2 2 0 0 0 9 10.76z" />
|
|
276
|
-
</>
|
|
277
|
-
),
|
|
278
|
-
// ---- input / action ----
|
|
279
|
-
// Magnifying glass — search fields, find actions.
|
|
280
|
-
search: (
|
|
281
|
-
<>
|
|
282
|
-
<circle cx="11" cy="11" r="8" />
|
|
283
|
-
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
284
|
-
</>
|
|
285
|
-
),
|
|
286
|
-
// Microphone — voice input, dictation.
|
|
287
|
-
mic: (
|
|
288
|
-
<>
|
|
289
|
-
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
|
|
290
|
-
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
291
|
-
<line x1="12" y1="19" x2="12" y2="23" />
|
|
292
|
-
<line x1="8" y1="23" x2="16" y2="23" />
|
|
293
|
-
</>
|
|
294
|
-
),
|
|
295
|
-
// ---- color mode ----
|
|
296
|
-
sun: (
|
|
297
|
-
<>
|
|
298
|
-
<circle cx="12" cy="12" r="5" />
|
|
299
|
-
<line x1="12" y1="1" x2="12" y2="3" />
|
|
300
|
-
<line x1="12" y1="21" x2="12" y2="23" />
|
|
301
|
-
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
|
302
|
-
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
|
303
|
-
<line x1="1" y1="12" x2="3" y2="12" />
|
|
304
|
-
<line x1="21" y1="12" x2="23" y2="12" />
|
|
305
|
-
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
|
306
|
-
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
|
307
|
-
</>
|
|
308
|
-
),
|
|
309
|
-
moon: <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />,
|
|
310
|
-
monitor: (
|
|
311
|
-
<>
|
|
312
|
-
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
|
313
|
-
<line x1="8" y1="21" x2="16" y2="21" />
|
|
314
|
-
<line x1="12" y1="17" x2="12" y2="21" />
|
|
315
|
-
</>
|
|
316
|
-
),
|
|
317
|
-
// Circle, half solid — high-contrast / mode indicator.
|
|
318
|
-
contrast: (
|
|
319
|
-
<>
|
|
320
|
-
<circle cx="12" cy="12" r="10" />
|
|
321
|
-
<path d="M12 2a10 10 0 0 1 0 20z" fill="currentColor" stroke="none" />
|
|
322
|
-
</>
|
|
323
|
-
),
|
|
324
|
-
// ---- device status ----
|
|
325
|
-
// Four ascending bars — cellular signal strength.
|
|
326
|
-
signal: (
|
|
327
|
-
<>
|
|
328
|
-
<line x1="4" y1="20" x2="4" y2="16" />
|
|
329
|
-
<line x1="9" y1="20" x2="9" y2="12" />
|
|
330
|
-
<line x1="14" y1="20" x2="14" y2="8" />
|
|
331
|
-
<line x1="19" y1="20" x2="19" y2="4" />
|
|
332
|
-
</>
|
|
333
|
-
),
|
|
334
|
-
// Horizontal battery outline with a terminal nub — charge level.
|
|
335
|
-
battery: (
|
|
336
|
-
<>
|
|
337
|
-
<rect x="2" y="7" width="17" height="10" rx="2" />
|
|
338
|
-
<line x1="22" y1="11" x2="22" y2="13" />
|
|
339
|
-
</>
|
|
340
|
-
),
|
|
341
|
-
// ---- media ----
|
|
342
|
-
// Camera body with a side lens — video file or clip.
|
|
343
|
-
video: (
|
|
344
|
-
<>
|
|
345
|
-
<rect x="2" y="6" width="14" height="12" rx="2" />
|
|
346
|
-
<path d="M16 10l6-3v10l-6-3z" />
|
|
347
|
-
</>
|
|
348
|
-
),
|
|
349
|
-
// Beamed note — audio file or track.
|
|
350
|
-
music: (
|
|
351
|
-
<>
|
|
352
|
-
<path d="M9 18V5l12-2v13" />
|
|
353
|
-
<circle cx="6" cy="18" r="3" />
|
|
354
|
-
<circle cx="18" cy="16" r="3" />
|
|
355
|
-
</>
|
|
356
|
-
),
|
|
357
|
-
// Right-pointing triangle — start playback.
|
|
358
|
-
play: <path d="M6 4l14 8-14 8z" />,
|
|
359
|
-
// Two vertical bars — pause playback.
|
|
360
|
-
pause: (
|
|
361
|
-
<>
|
|
362
|
-
<rect x="6" y="4" width="4" height="16" />
|
|
363
|
-
<rect x="14" y="4" width="4" height="16" />
|
|
364
|
-
</>
|
|
365
|
-
),
|
|
10
|
+
/** One geometry shape as the SVG element it was drawn as before 5.12. */
|
|
11
|
+
function svgShape(shape: Shape, key: number) {
|
|
12
|
+
const {tag, ...attrs} = shape
|
|
13
|
+
return createElement(tag, {key, ...attrs})
|
|
366
14
|
}
|
|
367
15
|
|
|
368
|
-
|
|
369
|
-
* Filled icons — solid shape with evenodd cutout for the inner symbol.
|
|
370
|
-
* The cutout reveals the background color, so they work on any theme.
|
|
371
|
-
* Uses fill="currentColor" only, no stroke.
|
|
372
|
-
*/
|
|
373
|
-
const filledIcons: Partial<Record<IconName, ReactNode>> = {
|
|
374
|
-
// Filled circle with "i" cutout (dot + line)
|
|
375
|
-
'info-circle-filled': (
|
|
376
|
-
<path
|
|
377
|
-
fillRule="evenodd"
|
|
378
|
-
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm-.75 6a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0zM11 11a1 1 0 1 1 2 0v5a1 1 0 1 1-2 0v-5z"
|
|
379
|
-
/>
|
|
380
|
-
),
|
|
381
|
-
// Filled triangle with "!" cutout
|
|
382
|
-
'alert-triangle-filled': (
|
|
383
|
-
<path
|
|
384
|
-
fillRule="evenodd"
|
|
385
|
-
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM11 10a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0v-3zm.25 6a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0z"
|
|
386
|
-
/>
|
|
387
|
-
),
|
|
388
|
-
// Filled circle with checkmark cutout
|
|
389
|
-
'check-circle-filled': (
|
|
390
|
-
<path
|
|
391
|
-
fillRule="evenodd"
|
|
392
|
-
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm4.707 8.707a1 1 0 0 0-1.414-1.414L11 13.586l-2.293-2.293a1 1 0 0 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l5-5z"
|
|
393
|
-
/>
|
|
394
|
-
),
|
|
395
|
-
// Filled circle with X cutout
|
|
396
|
-
'x-circle-filled': (
|
|
397
|
-
<path
|
|
398
|
-
fillRule="evenodd"
|
|
399
|
-
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zM9.707 8.293a1 1 0 0 0-1.414 1.414L10.586 12l-2.293 2.293a1 1 0 1 0 1.414 1.414L12 13.414l2.293 2.293a1 1 0 0 0 1.414-1.414L13.414 12l2.293-2.293a1 1 0 0 0-1.414-1.414L12 10.586 9.707 8.293z"
|
|
400
|
-
/>
|
|
401
|
-
),
|
|
402
|
-
// Filled shield with checkmark cutout
|
|
403
|
-
'shield-filled': (
|
|
404
|
-
<path
|
|
405
|
-
fillRule="evenodd"
|
|
406
|
-
d="M12 1L3 5v7c0 6.5 8.5 10.5 9 10.73.5-.23 9-4.23 9-10.73V5l-9-4zm3.707 8.707a1 1 0 0 0-1.414-1.414L11 11.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4z"
|
|
407
|
-
/>
|
|
408
|
-
),
|
|
409
|
-
// Filled lock body with stroke shackle
|
|
410
|
-
'lock-filled': (
|
|
411
|
-
<>
|
|
412
|
-
<path
|
|
413
|
-
d="M7 11V7a5 5 0 0 1 10 0v4"
|
|
414
|
-
fill="none"
|
|
415
|
-
stroke="currentColor"
|
|
416
|
-
strokeWidth="2"
|
|
417
|
-
strokeLinecap="round"
|
|
418
|
-
/>
|
|
419
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
|
|
420
|
-
</>
|
|
421
|
-
),
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
interface IconProps {
|
|
425
|
-
name: IconName
|
|
426
|
-
/** Token from the icon size scale: sm 16, md 18, lg 24, xl 36, xxl 48. */
|
|
427
|
-
size?: IconSize
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
export function Icon({name, size: sizeToken = 'lg'}: IconProps) {
|
|
16
|
+
export function Icon({name, size: sizeToken = 'lg', color}: IconProps) {
|
|
431
17
|
const size = ICON_SIZES[sizeToken]
|
|
432
|
-
const
|
|
433
|
-
|
|
18
|
+
const glyph = GLYPHS[name]
|
|
19
|
+
const shapes = glyph.shapes.map(svgShape)
|
|
20
|
+
const style = color ? {color: colors[color]} : undefined
|
|
21
|
+
if (glyph.style === 'fill') {
|
|
434
22
|
return (
|
|
435
|
-
<svg viewBox="0 0 24 24" fill="currentColor" width={size} height={size}>
|
|
436
|
-
{
|
|
23
|
+
<svg viewBox="0 0 24 24" fill="currentColor" width={size} height={size} style={style}>
|
|
24
|
+
{shapes}
|
|
437
25
|
</svg>
|
|
438
26
|
)
|
|
439
27
|
}
|
|
@@ -448,8 +36,9 @@ export function Icon({name, size: sizeToken = 'lg'}: IconProps) {
|
|
|
448
36
|
strokeLinejoin="round"
|
|
449
37
|
width={size}
|
|
450
38
|
height={size}
|
|
39
|
+
style={style}
|
|
451
40
|
>
|
|
452
|
-
{
|
|
41
|
+
{shapes}
|
|
453
42
|
</svg>
|
|
454
43
|
)
|
|
455
44
|
}
|