@dfosco/storyboard-react 4.2.0-beta.0 → 4.2.0-beta.17
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/package.json +5 -4
- package/src/AuthModal/AuthModal.jsx +6 -2
- package/src/BranchBar/BranchBar.jsx +17 -5
- package/src/BranchBar/BranchBar.module.css +11 -2
- package/src/CommandPalette/CommandPalette.jsx +267 -164
- package/src/CommandPalette/command-palette.css +130 -78
- package/src/Icon.jsx +112 -48
- package/src/Viewfinder.jsx +511 -61
- package/src/Viewfinder.module.css +414 -2
- package/src/canvas/CanvasPage.bridge.test.jsx +14 -6
- package/src/canvas/CanvasPage.dragdrop.test.jsx +10 -6
- package/src/canvas/CanvasPage.jsx +157 -174
- package/src/canvas/CanvasPage.module.css +0 -15
- package/src/canvas/CanvasPage.multiselect.test.jsx +10 -6
- package/src/canvas/ConnectorLayer.jsx +5 -5
- package/src/canvas/PageSelector.test.jsx +15 -6
- package/src/canvas/useCanvas.js +1 -1
- package/src/canvas/widgets/ActionWidget.jsx +200 -0
- package/src/canvas/widgets/ActionWidget.module.css +122 -0
- package/src/canvas/widgets/FigmaEmbed.jsx +97 -29
- package/src/canvas/widgets/FigmaEmbed.module.css +61 -0
- package/src/canvas/widgets/ImageWidget.jsx +1 -1
- package/src/canvas/widgets/LinkPreview.jsx +64 -5
- package/src/canvas/widgets/LinkPreview.module.css +127 -0
- package/src/canvas/widgets/MarkdownBlock.jsx +39 -17
- package/src/canvas/widgets/MarkdownBlock.module.css +123 -0
- package/src/canvas/widgets/PrototypeEmbed.jsx +183 -20
- package/src/canvas/widgets/PrototypeEmbed.module.css +117 -0
- package/src/canvas/widgets/PrototypeEmbed.test.jsx +2 -2
- package/src/canvas/widgets/SplitExpandModal.jsx +234 -0
- package/src/canvas/widgets/SplitExpandModal.module.css +335 -0
- package/src/canvas/widgets/SplitScreenTopBar.jsx +30 -0
- package/src/canvas/widgets/SplitScreenTopBar.module.css +58 -0
- package/src/canvas/widgets/StoryWidget.jsx +7 -4
- package/src/canvas/widgets/TerminalReadWidget.jsx +140 -0
- package/src/canvas/widgets/TerminalReadWidget.module.css +92 -0
- package/src/canvas/widgets/TerminalWidget.jsx +299 -49
- package/src/canvas/widgets/TerminalWidget.module.css +155 -1
- package/src/canvas/widgets/WidgetChrome.jsx +19 -14
- package/src/canvas/widgets/WidgetChrome.module.css +10 -0
- package/src/canvas/widgets/embedInteraction.test.jsx +24 -26
- package/src/canvas/widgets/expandUtils.js +188 -0
- package/src/canvas/widgets/index.js +5 -0
- package/src/canvas/widgets/snapshotDisplay.test.jsx +23 -71
- package/src/canvas/widgets/widgetConfig.js +19 -1
- package/src/hooks/useConfig.js +14 -0
- package/src/index.js +4 -0
- package/src/vite/data-plugin.js +264 -14
|
@@ -1,116 +1,168 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Styles for cmdk command palette.
|
|
3
|
+
* cmdk is headless — all visual styling is provided here.
|
|
4
|
+
* Uses [cmdk-*] attribute selectors and data-color-mode for theming.
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
/* ─── Overlay ─── */
|
|
8
|
+
[cmdk-overlay] {
|
|
9
|
+
position: fixed;
|
|
10
|
+
inset: 0;
|
|
11
|
+
background: rgba(0, 0, 0, 0.5);
|
|
12
|
+
z-index: 10000;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* ─── Dialog content (Radix Content wrapper) ─── */
|
|
16
|
+
[cmdk-dialog] {
|
|
17
|
+
position: fixed;
|
|
18
|
+
top: 20%;
|
|
19
|
+
left: 50%;
|
|
20
|
+
transform: translateX(-50%);
|
|
21
|
+
width: calc(100% - 2rem);
|
|
22
|
+
max-width: 560px;
|
|
23
|
+
z-index: 10001;
|
|
24
|
+
border-radius: 12px;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
27
|
+
background: var(--bgColor-default, #ffffff);
|
|
28
|
+
color: var(--fgColor-default, #1f2328);
|
|
29
|
+
border: 1px solid var(--borderColor-muted, #d1d9e0);
|
|
7
30
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
31
|
+
outline: none;
|
|
8
32
|
}
|
|
9
33
|
|
|
10
|
-
/*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
34
|
+
/* ─── Input ─── */
|
|
35
|
+
[cmdk-input] {
|
|
36
|
+
width: 100%;
|
|
37
|
+
padding: 14px 16px;
|
|
38
|
+
font-size: 15px;
|
|
39
|
+
border: none;
|
|
40
|
+
border-bottom: 1px solid var(--borderColor-muted, #d1d9e0);
|
|
41
|
+
outline: none;
|
|
42
|
+
background: transparent;
|
|
43
|
+
color: var(--fgColor-default, #1f2328);
|
|
44
|
+
font-family: inherit;
|
|
20
45
|
}
|
|
21
46
|
|
|
22
|
-
|
|
23
|
-
color:
|
|
47
|
+
[cmdk-input]::placeholder {
|
|
48
|
+
color: var(--fgColor-muted, #656d76);
|
|
24
49
|
}
|
|
25
50
|
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
/* ─── List ─── */
|
|
52
|
+
[cmdk-list] {
|
|
53
|
+
max-height: 400px;
|
|
54
|
+
overflow-y: auto;
|
|
55
|
+
padding: 8px;
|
|
28
56
|
}
|
|
29
57
|
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
/* ─── Group ─── */
|
|
59
|
+
[cmdk-group-heading] {
|
|
60
|
+
font-size: 11px;
|
|
61
|
+
font-weight: 600;
|
|
62
|
+
text-transform: uppercase;
|
|
63
|
+
letter-spacing: 0.05em;
|
|
64
|
+
color: var(--fgColor-muted, #656d76);
|
|
65
|
+
padding: 8px 8px 4px;
|
|
66
|
+
user-select: none;
|
|
32
67
|
}
|
|
33
68
|
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
/* ─── Item ─── */
|
|
70
|
+
[cmdk-item] {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
padding: 8px 10px;
|
|
75
|
+
border-radius: 6px;
|
|
76
|
+
font-size: 14px;
|
|
77
|
+
/* font-weight: 500; */
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
user-select: none;
|
|
80
|
+
color: var(--fgColor-default, #1f2328);
|
|
81
|
+
transition: background 0.1s;
|
|
36
82
|
}
|
|
37
83
|
|
|
38
|
-
|
|
39
|
-
|
|
84
|
+
[cmdk-item][data-selected="true"] {
|
|
85
|
+
background: var(--bgColor-neutral-muted, rgba(175, 184, 193, 0.2));
|
|
40
86
|
}
|
|
41
87
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* react-cmdk uses @media (prefers-color-scheme: dark) internally,
|
|
45
|
-
* but we drive dark mode via data-color-mode on <html> (set by themeStore).
|
|
46
|
-
* Primer may also mirror it to <body>, so we target both.
|
|
47
|
-
*/
|
|
48
|
-
body[data-color-mode="dark"] .command-palette,
|
|
49
|
-
html[data-color-mode="dark"] .command-palette {
|
|
50
|
-
color-scheme: dark;
|
|
88
|
+
[cmdk-item]:active {
|
|
89
|
+
background: var(--bgColor-neutral-muted, rgba(175, 184, 193, 0.3));
|
|
51
90
|
}
|
|
52
91
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
92
|
+
/* ─── Separator ─── */
|
|
93
|
+
[cmdk-separator] {
|
|
94
|
+
height: 1px;
|
|
95
|
+
margin: 4px 14px;
|
|
96
|
+
background: var(--borderColor-muted, #d1d9e0);
|
|
56
97
|
}
|
|
57
98
|
|
|
58
|
-
/*
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
99
|
+
/* ─── Empty state ─── */
|
|
100
|
+
[cmdk-empty] {
|
|
101
|
+
padding: 2rem;
|
|
102
|
+
text-align: center;
|
|
103
|
+
color: var(--fgColor-muted, #656d76);
|
|
104
|
+
font-size: 14px;
|
|
62
105
|
}
|
|
63
106
|
|
|
64
|
-
/*
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
background
|
|
107
|
+
/* ─── Dark mode ─── */
|
|
108
|
+
html[data-color-mode="dark"] [cmdk-dialog],
|
|
109
|
+
body[data-color-mode="dark"] [cmdk-dialog] {
|
|
110
|
+
background: #2d333b;
|
|
111
|
+
color: #e6edf3;
|
|
112
|
+
border-color: #444c56;
|
|
68
113
|
}
|
|
69
114
|
|
|
70
|
-
|
|
71
|
-
body[data-color-mode="dark"]
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
html[data-color-mode="dark"] [cmdk-input],
|
|
116
|
+
body[data-color-mode="dark"] [cmdk-input] {
|
|
117
|
+
color: #e6edf3;
|
|
118
|
+
border-bottom-color: #444c56;
|
|
74
119
|
}
|
|
75
120
|
|
|
76
|
-
|
|
77
|
-
body[data-color-mode="dark"]
|
|
78
|
-
|
|
79
|
-
html[data-color-mode="dark"] .command-palette .text-gray-400,
|
|
80
|
-
html[data-color-mode="dark"] .command-palette .text-gray-500 {
|
|
81
|
-
color: #768390 !important;
|
|
121
|
+
html[data-color-mode="dark"] [cmdk-input]::placeholder,
|
|
122
|
+
body[data-color-mode="dark"] [cmdk-input]::placeholder {
|
|
123
|
+
color: #636e7b;
|
|
82
124
|
}
|
|
83
125
|
|
|
84
|
-
|
|
85
|
-
body[data-color-mode="dark"]
|
|
86
|
-
|
|
87
|
-
color: #e6edf3 !important;
|
|
126
|
+
html[data-color-mode="dark"] [cmdk-group-heading],
|
|
127
|
+
body[data-color-mode="dark"] [cmdk-group-heading] {
|
|
128
|
+
color: #768390;
|
|
88
129
|
}
|
|
89
|
-
|
|
90
|
-
html[data-color-mode="dark"]
|
|
91
|
-
|
|
130
|
+
|
|
131
|
+
html[data-color-mode="dark"] [cmdk-item],
|
|
132
|
+
body[data-color-mode="dark"] [cmdk-item] {
|
|
133
|
+
color: #e6edf3;
|
|
92
134
|
}
|
|
93
|
-
|
|
94
|
-
html[data-color-mode="dark"]
|
|
95
|
-
|
|
135
|
+
|
|
136
|
+
html[data-color-mode="dark"] [cmdk-item][data-selected="true"],
|
|
137
|
+
body[data-color-mode="dark"] [cmdk-item][data-selected="true"] {
|
|
138
|
+
background: rgba(99, 110, 123, 0.4);
|
|
96
139
|
}
|
|
97
140
|
|
|
98
|
-
|
|
99
|
-
body[data-color-mode="dark"]
|
|
100
|
-
|
|
101
|
-
body[data-color-mode="dark"] .command-palette .border-b,
|
|
102
|
-
html[data-color-mode="dark"] .command-palette .divide-y > :not([hidden]) ~ :not([hidden]),
|
|
103
|
-
html[data-color-mode="dark"] .command-palette .border-t,
|
|
104
|
-
html[data-color-mode="dark"] .command-palette .border-b {
|
|
105
|
-
border-color: #444c56 !important;
|
|
141
|
+
html[data-color-mode="dark"] [cmdk-separator],
|
|
142
|
+
body[data-color-mode="dark"] [cmdk-separator] {
|
|
143
|
+
background: #444c56;
|
|
106
144
|
}
|
|
107
145
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
146
|
+
html[data-color-mode="dark"] [cmdk-empty],
|
|
147
|
+
body[data-color-mode="dark"] [cmdk-empty] {
|
|
148
|
+
color: #768390;
|
|
111
149
|
}
|
|
112
150
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
151
|
+
html[data-color-mode="dark"] [cmdk-overlay],
|
|
152
|
+
body[data-color-mode="dark"] [cmdk-overlay] {
|
|
153
|
+
background: rgba(0, 0, 0, 0.7);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ─── Mobile responsiveness ─── */
|
|
157
|
+
@media (max-width: 640px) {
|
|
158
|
+
[cmdk-dialog] {
|
|
159
|
+
max-width: 100%;
|
|
160
|
+
border-radius: 0;
|
|
161
|
+
top: 0;
|
|
162
|
+
width: 100%;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[cmdk-list] {
|
|
166
|
+
max-height: 70vh;
|
|
167
|
+
}
|
|
116
168
|
}
|
package/src/Icon.jsx
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Icon —
|
|
2
|
+
* Icon — renders icons from multiple sources using namespaced names.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* primer/ → Primer Octicons (fill-based)
|
|
5
|
+
* feather/ → Feather Icons (stroke-based)
|
|
5
6
|
* iconoir/ → Iconoir (stroke-based, manually registered)
|
|
6
|
-
* (no prefix) → Custom
|
|
7
|
+
* (no prefix) → Custom (folder, prototype, canvas, component, etc.)
|
|
7
8
|
*
|
|
8
9
|
* Usage:
|
|
9
|
-
* <Icon name="
|
|
10
|
-
* <Icon name="
|
|
10
|
+
* <Icon name="primer/repo" />
|
|
11
|
+
* <Icon name="feather/flag" size={16} />
|
|
12
|
+
* <Icon name="iconoir/key-command" size={16} strokeWeight={2} />
|
|
11
13
|
* <Icon name="prototype" size={14} />
|
|
12
|
-
* <Icon name="
|
|
14
|
+
* <Icon name="feather/tablet" rotate={90} />
|
|
15
|
+
* <Icon name="primer/lock" offsetX={1} offsetY={-1} />
|
|
16
|
+
* <Icon name="feather/arrow-right" flipX />
|
|
13
17
|
*/
|
|
14
18
|
|
|
15
19
|
/* ─── Custom SVG paths (fill-based, no namespace prefix) ─── */
|
|
16
20
|
|
|
17
21
|
const customIcons = {
|
|
22
|
+
'home': {
|
|
23
|
+
viewBox: '0 0 16 16',
|
|
24
|
+
path: 'M6.906.664a1.749 1.749 0 0 1 2.187 0l5.25 4.2c.415.332.657.835.657 1.367v7.019A1.75 1.75 0 0 1 13.25 15h-3.5a.75.75 0 0 1-.75-.75V9H7v5.25a.75.75 0 0 1-.75.75h-3.5A1.75 1.75 0 0 1 1 13.25V6.23c0-.531.242-1.034.657-1.366l5.25-4.2Zm1.25 1.171a.25.25 0 0 0-.312 0l-5.25 4.2a.25.25 0 0 0-.094.196v7.019c0 .138.112.25.25.25H5.5V8.25a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 .75.75v5.25h2.75a.25.25 0 0 0 .25-.25V6.23a.25.25 0 0 0-.094-.195Z',
|
|
25
|
+
},
|
|
18
26
|
'folder': {
|
|
19
27
|
viewBox: '0 0 24 24',
|
|
20
28
|
path: 'M4 20q-.825 0-1.412-.587T2 18V6q0-.825.588-1.412T4 4h5.175q.4 0 .763.15t.637.425L12 6h8q.825 0 1.413.588T22 8v10q0 .825-.587 1.413T20 20z',
|
|
@@ -52,6 +60,14 @@ const customIcons = {
|
|
|
52
60
|
'M17.6103 6.5C18.2731 6.5 18.8104 7.03731 18.8104 7.70012C18.8104 8.36293 18.2731 8.90025 17.6103 8.90025H17.5986C16.9358 8.90025 16.3984 8.36293 16.3984 7.70012C16.3984 7.03731 16.9358 6.5 17.5986 6.5H17.6103Z',
|
|
53
61
|
],
|
|
54
62
|
},
|
|
63
|
+
'agents': {
|
|
64
|
+
viewBox: '0 0 32 32',
|
|
65
|
+
path: 'M27.2 16c0-6.19-5.01-11.2-11.2-11.2S4.8 9.81 4.8 16S9.81 27.2 16 27.2S27.2 22.19 27.2 16m-5.6 2.1a1.4 1.4 0 0 1 0 2.8h-4.2a1.4 1.4 0 0 1 0-2.8zm-11.2-6.8a1.397 1.397 0 0 1 1.84.361l.08.119l2.1 3.5l.087.171a1.4 1.4 0 0 1 0 1.1l-.088.171l-2.1 3.5a1.4 1.4 0 0 1-2.4-1.44l1.67-2.78l-1.67-2.78l-.067-.127a1.394 1.394 0 0 1 .547-1.79zM30 16c0 7.73-6.27 14-14 14S2 23.73 2 16S8.27 2 16 2s14 6.27 14 14',
|
|
66
|
+
},
|
|
67
|
+
'claude': {
|
|
68
|
+
viewBox: '0 0 24 24',
|
|
69
|
+
path: 'm4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z',
|
|
70
|
+
},
|
|
55
71
|
'flow': {
|
|
56
72
|
viewBox: '0 0 24 24',
|
|
57
73
|
strokeWidth: '2.5',
|
|
@@ -95,6 +111,11 @@ const iconoirIcons = {
|
|
|
95
111
|
strokeWidth: '1.5',
|
|
96
112
|
content: '<path d="M14 20.4V14.6C14 14.2686 14.2686 14 14.6 14H20.4C20.7314 14 21 14.2686 21 14.6V20.4C21 20.7314 20.7314 21 20.4 21H14.6C14.2686 21 14 20.7314 14 20.4Z" stroke="currentColor" stroke-width="1.5"/><path d="M3 20.4V14.6C3 14.2686 3.26863 14 3.6 14H9.4C9.73137 14 10 14.2686 10 14.6V20.4C10 20.7314 9.73137 21 9.4 21H3.6C3.26863 21 3 20.7314 3 20.4Z" stroke="currentColor" stroke-width="1.5"/><path d="M14 9.4V3.6C14 3.26863 14.2686 3 14.6 3H20.4C20.7314 3 21 3.26863 21 3.6V9.4C21 9.73137 20.7314 10 20.4 10H14.6C14.2686 10 14 9.73137 14 9.4Z" stroke="currentColor" stroke-width="1.5"/><path d="M3 9.4V3.6C3 3.26863 3.26863 3 3.6 3H9.4C9.73137 3 10 3.26863 10 3.6V9.4C10 9.73137 9.73137 10 9.4 10H3.6C3.26863 10 3 9.73137 3 9.4Z" stroke="currentColor" stroke-width="1.5"/>',
|
|
97
113
|
},
|
|
114
|
+
'select-point-3d': {
|
|
115
|
+
viewBox: '0 0 24 24',
|
|
116
|
+
strokeWidth: '1.5',
|
|
117
|
+
content: '<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 7.35304L21 16.647C21 16.8649 20.8819 17.0656 20.6914 17.1715L12.2914 21.8381C12.1102 21.9388 11.8898 21.9388 11.7086 21.8381L3.30861 17.1715C3.11814 17.0656 3 16.8649 3 16.647L2.99998 7.35304C2.99998 7.13514 3.11812 6.93437 3.3086 6.82855L11.7086 2.16188C11.8898 2.06121 12.1102 2.06121 12.2914 2.16188L20.6914 6.82855C20.8818 6.93437 21 7.13514 21 7.35304Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>',
|
|
118
|
+
},
|
|
98
119
|
'square-3d-three-points': {
|
|
99
120
|
viewBox: '0 0 24 24',
|
|
100
121
|
strokeWidth: '1.5',
|
|
@@ -104,77 +125,120 @@ const iconoirIcons = {
|
|
|
104
125
|
|
|
105
126
|
/* ─── React Component ─── */
|
|
106
127
|
|
|
128
|
+
import octicons from '@primer/octicons'
|
|
129
|
+
import feather from 'feather-icons'
|
|
130
|
+
|
|
107
131
|
/**
|
|
108
132
|
* @param {object} props
|
|
109
|
-
* @param {string} props.name -
|
|
133
|
+
* @param {string} props.name - Namespaced icon name: primer/, feather/, iconoir/, or plain custom name
|
|
110
134
|
* @param {number} [props.size=16]
|
|
111
135
|
* @param {string} [props.label] - Accessible label (sets aria-label instead of aria-hidden)
|
|
112
136
|
* @param {string} [props.color]
|
|
137
|
+
* @param {number} [props.offsetX=0]
|
|
138
|
+
* @param {number} [props.offsetY=0]
|
|
139
|
+
* @param {number} [props.rotate=0]
|
|
140
|
+
* @param {boolean} [props.flipX=false]
|
|
141
|
+
* @param {boolean} [props.flipY=false]
|
|
113
142
|
* @param {number} [props.strokeWeight] - Override stroke width
|
|
143
|
+
* @param {number} [props.scale=1]
|
|
114
144
|
* @param {string} [props.className]
|
|
115
145
|
*/
|
|
116
|
-
export default function Icon({
|
|
146
|
+
export default function Icon({
|
|
147
|
+
name, size = 16, label, color,
|
|
148
|
+
offsetX = 0, offsetY = 0, rotate = 0,
|
|
149
|
+
flipX = false, flipY = false,
|
|
150
|
+
strokeWeight, scale = 1, className,
|
|
151
|
+
}) {
|
|
117
152
|
const source = name.includes('/') ? name.split('/')[0] : null
|
|
118
153
|
const iconName = name.includes('/') ? name.slice(name.indexOf('/') + 1) : name
|
|
119
154
|
|
|
120
155
|
const ariaProps = label ? { 'aria-label': label, role: 'img' } : { 'aria-hidden': true }
|
|
121
|
-
const style = color ? { color } : undefined
|
|
122
156
|
|
|
123
|
-
//
|
|
157
|
+
// Build wrapper style with all transform props
|
|
158
|
+
const scaleX = (flipX ? -1 : 1) * scale
|
|
159
|
+
const scaleY = (flipY ? -1 : 1) * scale
|
|
160
|
+
const hasTransform = offsetX || offsetY || rotate || flipX || flipY || scale !== 1
|
|
161
|
+
const wrapperStyle = {
|
|
162
|
+
...(color ? { color } : {}),
|
|
163
|
+
display: 'inline-flex',
|
|
164
|
+
...(hasTransform ? {
|
|
165
|
+
translate: (offsetX || offsetY) ? `${offsetX}px ${offsetY}px` : undefined,
|
|
166
|
+
rotate: rotate ? `${rotate}deg` : undefined,
|
|
167
|
+
scale: (flipX || flipY || scale !== 1) ? `${scaleX} ${scaleY}` : undefined,
|
|
168
|
+
} : {}),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let svgContent = null
|
|
172
|
+
|
|
173
|
+
// Custom icons (no source prefix)
|
|
124
174
|
const custom = !source ? customIcons[iconName] : null
|
|
125
175
|
if (custom) {
|
|
126
|
-
// Simple fill-based path icon
|
|
127
176
|
if (custom.path) {
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
</svg>
|
|
133
|
-
</span>
|
|
177
|
+
svgContent = (
|
|
178
|
+
<svg width={size} height={size} viewBox={custom.viewBox} fill="currentColor" {...ariaProps}>
|
|
179
|
+
<path d={custom.path} />
|
|
180
|
+
</svg>
|
|
134
181
|
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
<svg width={size} height={size} viewBox={custom.viewBox} fill="none" stroke="currentColor" strokeWidth={custom.strokeWidth || "1.5"} strokeLinecap="round" strokeLinejoin="round" {...ariaProps}>
|
|
141
|
-
{custom.strokePaths.map((d, i) => <path key={i} d={d} />)}
|
|
142
|
-
</svg>
|
|
143
|
-
</span>
|
|
182
|
+
} else if (custom.strokePaths) {
|
|
183
|
+
svgContent = (
|
|
184
|
+
<svg width={size} height={size} viewBox={custom.viewBox} fill="none" stroke="currentColor" strokeWidth={strokeWeight ?? custom.strokeWidth ?? '1.5'} strokeLinecap="round" strokeLinejoin="round" {...ariaProps}>
|
|
185
|
+
{custom.strokePaths.map((d, i) => <path key={i} d={d} />)}
|
|
186
|
+
</svg>
|
|
144
187
|
)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{custom.strokeRect && <rect {...custom.strokeRect} />}
|
|
152
|
-
{custom.fillPaths?.map((d, i) => <path key={i} d={d} fill="currentColor" stroke="none" />)}
|
|
153
|
-
</svg>
|
|
154
|
-
</span>
|
|
188
|
+
} else if (custom.strokeRect || custom.fillPaths) {
|
|
189
|
+
svgContent = (
|
|
190
|
+
<svg width={size} height={size} viewBox={custom.viewBox} fill="none" stroke="currentColor" strokeWidth={strokeWeight ?? '2'} {...ariaProps}>
|
|
191
|
+
{custom.strokeRect && <rect {...custom.strokeRect} />}
|
|
192
|
+
{custom.fillPaths?.map((d, i) => <path key={i} d={d} fill="currentColor" stroke="none" />)}
|
|
193
|
+
</svg>
|
|
155
194
|
)
|
|
156
195
|
}
|
|
157
196
|
}
|
|
158
197
|
|
|
198
|
+
// Primer Octicons
|
|
199
|
+
if (!svgContent && source === 'primer') {
|
|
200
|
+
const octicon = octicons[iconName]
|
|
201
|
+
if (octicon) {
|
|
202
|
+
const html = octicon.toSVG({
|
|
203
|
+
width: size, height: size,
|
|
204
|
+
fill: 'currentColor',
|
|
205
|
+
...(label ? { 'aria-label': label } : { 'aria-hidden': 'true' }),
|
|
206
|
+
})
|
|
207
|
+
svgContent = <span dangerouslySetInnerHTML={{ __html: html }} />
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Feather Icons
|
|
212
|
+
if (!svgContent && source === 'feather') {
|
|
213
|
+
const icon = feather.icons[iconName]
|
|
214
|
+
if (icon) {
|
|
215
|
+
const html = icon.toSvg({
|
|
216
|
+
width: size, height: size,
|
|
217
|
+
'stroke-width': strokeWeight ?? 2,
|
|
218
|
+
...(label ? { 'aria-label': label } : { 'aria-hidden': 'true' }),
|
|
219
|
+
})
|
|
220
|
+
svgContent = <span dangerouslySetInnerHTML={{ __html: html }} />
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
159
224
|
// Iconoir icons
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
225
|
+
if (!svgContent && source === 'iconoir') {
|
|
226
|
+
const iconoir = iconoirIcons[iconName]
|
|
227
|
+
if (iconoir) {
|
|
228
|
+
const sw = strokeWeight ?? iconoir.strokeWidth
|
|
229
|
+
svgContent = (
|
|
165
230
|
<svg
|
|
166
|
-
width={size}
|
|
167
|
-
height={size}
|
|
168
|
-
viewBox={iconoir.viewBox}
|
|
231
|
+
width={size} height={size} viewBox={iconoir.viewBox}
|
|
169
232
|
fill={iconoir.fill ? 'currentColor' : 'none'}
|
|
170
233
|
strokeWidth={iconoir.fill ? undefined : sw}
|
|
171
234
|
{...ariaProps}
|
|
172
235
|
dangerouslySetInnerHTML={{ __html: iconoir.content }}
|
|
173
236
|
/>
|
|
174
|
-
|
|
175
|
-
|
|
237
|
+
)
|
|
238
|
+
}
|
|
176
239
|
}
|
|
177
240
|
|
|
178
|
-
|
|
179
|
-
|
|
241
|
+
if (!svgContent) return null
|
|
242
|
+
|
|
243
|
+
return <span className={className} style={wrapperStyle}>{svgContent}</span>
|
|
180
244
|
}
|