@oztix/roadie-components 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Accordion.d.ts +37 -0
- package/dist/Accordion.js +3 -0
- package/dist/Accordion.js.map +1 -0
- package/dist/Autocomplete.d.ts +131 -0
- package/dist/Autocomplete.js +3 -0
- package/dist/Autocomplete.js.map +1 -0
- package/dist/Badge.d.ts +22 -0
- package/dist/Badge.js +2 -0
- package/dist/Badge.js.map +1 -0
- package/dist/Breadcrumb.d.ts +43 -0
- package/dist/Breadcrumb.js +2 -0
- package/dist/Breadcrumb.js.map +1 -0
- package/dist/Button-DagX1D_q.d.ts +19 -0
- package/dist/Button.d.ts +11 -15
- package/dist/Button.js +1 -1
- package/dist/Card.d.ts +51 -0
- package/dist/Card.js +2 -0
- package/dist/Card.js.map +1 -0
- package/dist/Code.d.ts +14 -24
- package/dist/Code.js +1 -1
- package/dist/Combobox.d.ts +137 -0
- package/dist/Combobox.js +3 -0
- package/dist/Combobox.js.map +1 -0
- package/dist/Field.d.ts +71 -0
- package/dist/Field.js +3 -0
- package/dist/Field.js.map +1 -0
- package/dist/Fieldset.d.ts +36 -0
- package/dist/Fieldset.js +3 -0
- package/dist/Fieldset.js.map +1 -0
- package/dist/Highlight.d.ts +14 -36
- package/dist/Highlight.js +1 -1
- package/dist/Indicator.d.ts +17 -0
- package/dist/Indicator.js +2 -0
- package/dist/Indicator.js.map +1 -0
- package/dist/Input.d.ts +18 -0
- package/dist/Input.js +3 -0
- package/dist/Input.js.map +1 -0
- package/dist/Label.d.ts +11 -0
- package/dist/Label.js +2 -0
- package/dist/Label.js.map +1 -0
- package/dist/LinkButton.d.ts +35 -0
- package/dist/LinkButton.js +2 -0
- package/dist/LinkButton.js.map +1 -0
- package/dist/Mark.d.ts +15 -26
- package/dist/Mark.js +1 -1
- package/dist/Marquee.d.ts +21 -0
- package/dist/Marquee.js +3 -0
- package/dist/Marquee.js.map +1 -0
- package/dist/Prose.d.ts +22 -0
- package/dist/Prose.js +2 -0
- package/dist/Prose.js.map +1 -0
- package/dist/RadioGroup.d.ts +59 -0
- package/dist/RadioGroup.js +3 -0
- package/dist/RadioGroup.js.map +1 -0
- package/dist/Select.d.ts +144 -0
- package/dist/Select.js +3 -0
- package/dist/Select.js.map +1 -0
- package/dist/Separator.d.ts +16 -0
- package/dist/Separator.js +2 -0
- package/dist/Separator.js.map +1 -0
- package/dist/SpotIllustration.d.ts +59 -24
- package/dist/SpotIllustration.js +2 -1
- package/dist/SpotIllustration.js.map +1 -1
- package/dist/Steps.d.ts +105 -0
- package/dist/Steps.js +3 -0
- package/dist/Steps.js.map +1 -0
- package/dist/Textarea.d.ts +19 -0
- package/dist/Textarea.js +3 -0
- package/dist/Textarea.js.map +1 -0
- package/dist/_chunks/chunk-2MBFDJ6K.js +3 -0
- package/dist/_chunks/chunk-2MBFDJ6K.js.map +1 -0
- package/dist/_chunks/chunk-3HWPLULJ.js +2 -0
- package/dist/_chunks/chunk-3HWPLULJ.js.map +1 -0
- package/dist/_chunks/chunk-3NU36NBL.js +3 -0
- package/dist/_chunks/chunk-3NU36NBL.js.map +1 -0
- package/dist/_chunks/chunk-42UB7PQB.js +3 -0
- package/dist/_chunks/chunk-42UB7PQB.js.map +1 -0
- package/dist/_chunks/chunk-4LGCF3SN.js +3 -0
- package/dist/_chunks/chunk-4LGCF3SN.js.map +1 -0
- package/dist/_chunks/chunk-A6JSYXKN.js +2 -0
- package/dist/_chunks/chunk-A6JSYXKN.js.map +1 -0
- package/dist/_chunks/chunk-AFSDN4WI.js +2 -0
- package/dist/_chunks/chunk-AFSDN4WI.js.map +1 -0
- package/dist/_chunks/chunk-DRVUAPKN.js +2 -0
- package/dist/_chunks/chunk-DRVUAPKN.js.map +1 -0
- package/dist/_chunks/chunk-EKOEXSAP.js +2 -0
- package/dist/_chunks/chunk-EKOEXSAP.js.map +1 -0
- package/dist/_chunks/chunk-FQNVMKKV.js +7 -0
- package/dist/_chunks/chunk-FQNVMKKV.js.map +1 -0
- package/dist/_chunks/chunk-FSO4EAAY.js +3 -0
- package/dist/_chunks/chunk-FSO4EAAY.js.map +1 -0
- package/dist/_chunks/chunk-IEDKSZAQ.js +3 -0
- package/dist/_chunks/chunk-IEDKSZAQ.js.map +1 -0
- package/dist/_chunks/chunk-JBHYUOI2.js +3 -0
- package/dist/_chunks/chunk-JBHYUOI2.js.map +1 -0
- package/dist/_chunks/chunk-JGTZ3GCR.js +2 -0
- package/dist/_chunks/chunk-JGTZ3GCR.js.map +1 -0
- package/dist/_chunks/chunk-LHNOY24C.js +2 -0
- package/dist/_chunks/chunk-LHNOY24C.js.map +1 -0
- package/dist/_chunks/chunk-LMV3JECI.js +3 -0
- package/dist/_chunks/chunk-LMV3JECI.js.map +1 -0
- package/dist/_chunks/chunk-M4FEKBLW.js +2 -0
- package/dist/_chunks/chunk-M4FEKBLW.js.map +1 -0
- package/dist/_chunks/chunk-MDRAL676.js +3 -0
- package/dist/_chunks/chunk-MDRAL676.js.map +1 -0
- package/dist/_chunks/chunk-N2HGY7W7.js +3 -0
- package/dist/_chunks/chunk-N2HGY7W7.js.map +1 -0
- package/dist/_chunks/chunk-OIAETOZT.js +3 -0
- package/dist/_chunks/chunk-OIAETOZT.js.map +1 -0
- package/dist/_chunks/chunk-RXMWFJ6W.js +3 -0
- package/dist/_chunks/chunk-RXMWFJ6W.js.map +1 -0
- package/dist/_chunks/chunk-VSKUGXQG.js +3 -0
- package/dist/_chunks/chunk-VSKUGXQG.js.map +1 -0
- package/dist/_chunks/chunk-WOU2B425.js +3 -0
- package/dist/_chunks/chunk-WOU2B425.js.map +1 -0
- package/dist/_chunks/chunk-Y6TDYPCZ.js +3 -0
- package/dist/_chunks/chunk-Y6TDYPCZ.js.map +1 -0
- package/dist/_chunks/chunk-ZXR32FYA.js +2 -0
- package/dist/_chunks/chunk-ZXR32FYA.js.map +1 -0
- package/dist/index.d.ts +60 -15
- package/dist/index.js +34 -1
- package/dist/index.js.map +1 -1
- package/package.json +18 -22
- package/dist/Container.d.ts +0 -34
- package/dist/Container.js +0 -2
- package/dist/Container.js.map +0 -1
- package/dist/Heading.d.ts +0 -45
- package/dist/Heading.js +0 -2
- package/dist/Heading.js.map +0 -1
- package/dist/Text.d.ts +0 -47
- package/dist/Text.js +0 -2
- package/dist/Text.js.map +0 -1
- package/dist/View.d.ts +0 -12
- package/dist/View.js +0 -2
- package/dist/View.js.map +0 -1
- package/dist/_chunks/chunk-AZZHYO2A.js +0 -3
- package/dist/_chunks/chunk-AZZHYO2A.js.map +0 -1
- package/dist/_chunks/chunk-JOQJCXYF.js +0 -2
- package/dist/_chunks/chunk-JOQJCXYF.js.map +0 -1
- package/dist/_chunks/chunk-NMGF2AP6.js +0 -2
- package/dist/_chunks/chunk-NMGF2AP6.js.map +0 -1
- package/dist/_chunks/chunk-OH4JYS35.js +0 -3
- package/dist/_chunks/chunk-OH4JYS35.js.map +0 -1
- package/dist/_chunks/chunk-P5L5LN6E.js +0 -2
- package/dist/_chunks/chunk-P5L5LN6E.js.map +0 -1
- package/dist/_chunks/chunk-RJEJUZ3O.js +0 -2
- package/dist/_chunks/chunk-RJEJUZ3O.js.map +0 -1
- package/dist/_chunks/chunk-SUDUTP6A.js +0 -3
- package/dist/_chunks/chunk-SUDUTP6A.js.map +0 -1
- package/dist/_chunks/chunk-YNF56IUK.js +0 -2
- package/dist/_chunks/chunk-YNF56IUK.js.map +0 -1
- package/dist/_chunks/chunk-ZXS7U3VJ.js +0 -2
- package/dist/_chunks/chunk-ZXS7U3VJ.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -27
- package/dist/hooks/index.js +0 -2
- package/dist/hooks/index.js.map +0 -1
- package/src/components/Button/Button.test.tsx +0 -156
- package/src/components/Button/Button.tsx +0 -12
- package/src/components/Button/IconButton.test.tsx +0 -234
- package/src/components/Button/IconButton.tsx +0 -14
- package/src/components/Button/index.tsx +0 -2
- package/src/components/Code/Code.test.tsx +0 -85
- package/src/components/Code/index.tsx +0 -37
- package/src/components/Container/Container.test.tsx +0 -241
- package/src/components/Container/index.tsx +0 -34
- package/src/components/Heading/Heading.test.tsx +0 -128
- package/src/components/Heading/index.tsx +0 -49
- package/src/components/Highlight/Highlight.test.tsx +0 -113
- package/src/components/Highlight/index.tsx +0 -96
- package/src/components/Mark/Mark.test.tsx +0 -82
- package/src/components/Mark/index.tsx +0 -33
- package/src/components/SpotIllustration/ArrowUpRight.tsx +0 -9
- package/src/components/SpotIllustration/CowboyHat.tsx +0 -6
- package/src/components/SpotIllustration/Cursor.tsx +0 -6
- package/src/components/SpotIllustration/FlowerSpiral.tsx +0 -9
- package/src/components/SpotIllustration/Football.tsx +0 -6
- package/src/components/SpotIllustration/Hand.tsx +0 -6
- package/src/components/SpotIllustration/Heart.tsx +0 -6
- package/src/components/SpotIllustration/HighFive.tsx +0 -6
- package/src/components/SpotIllustration/MapPin.tsx +0 -6
- package/src/components/SpotIllustration/NoteMusic.tsx +0 -6
- package/src/components/SpotIllustration/README.md +0 -280
- package/src/components/SpotIllustration/SpotIllustration.test.tsx +0 -179
- package/src/components/SpotIllustration/SpotIllustration.tsx +0 -96
- package/src/components/SpotIllustration/Ticket.tsx +0 -6
- package/src/components/SpotIllustration/WineGlass.tsx +0 -6
- package/src/components/SpotIllustration/createSpotIllustration.tsx +0 -46
- package/src/components/SpotIllustration/index.tsx +0 -42
- package/src/components/SpotIllustration/json/arrow-up-right.json +0 -34
- package/src/components/SpotIllustration/json/cowboy-hat.json +0 -34
- package/src/components/SpotIllustration/json/cursor.json +0 -34
- package/src/components/SpotIllustration/json/flower-spiral.json +0 -38
- package/src/components/SpotIllustration/json/football.json +0 -46
- package/src/components/SpotIllustration/json/hand.json +0 -22
- package/src/components/SpotIllustration/json/heart.json +0 -26
- package/src/components/SpotIllustration/json/high-five.json +0 -62
- package/src/components/SpotIllustration/json/map-pin.json +0 -26
- package/src/components/SpotIllustration/json/note-music.json +0 -42
- package/src/components/SpotIllustration/json/ticket.json +0 -42
- package/src/components/SpotIllustration/json/wine-glass.json +0 -34
- package/src/components/SpotIllustration/svgs/arrow-up-right.svg +0 -9
- package/src/components/SpotIllustration/svgs/cowboy-hat.svg +0 -9
- package/src/components/SpotIllustration/svgs/cursor.svg +0 -9
- package/src/components/SpotIllustration/svgs/flower-spiral.svg +0 -10
- package/src/components/SpotIllustration/svgs/football.svg +0 -12
- package/src/components/SpotIllustration/svgs/hand.svg +0 -6
- package/src/components/SpotIllustration/svgs/heart.svg +0 -7
- package/src/components/SpotIllustration/svgs/high-five.svg +0 -16
- package/src/components/SpotIllustration/svgs/map-pin.svg +0 -7
- package/src/components/SpotIllustration/svgs/note-music.svg +0 -11
- package/src/components/SpotIllustration/svgs/ticket.svg +0 -11
- package/src/components/SpotIllustration/svgs/wine-glass.svg +0 -9
- package/src/components/Text/Text.test.tsx +0 -121
- package/src/components/Text/index.tsx +0 -51
- package/src/components/View/View.test.tsx +0 -161
- package/src/components/View/index.tsx +0 -12
- package/src/components/index.ts +0 -17
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useColorMode.ts +0 -37
- package/src/index.test.tsx +0 -37
- package/src/index.tsx +0 -9
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
# SpotIllustration System
|
|
2
|
-
|
|
3
|
-
A fully automated system for creating themed SVG illustrations that adapt to the design system's color palette.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
### Adding a New Illustration
|
|
8
|
-
|
|
9
|
-
1. **Export from Figma**: Save your SVG to `svgs/your-name.svg`
|
|
10
|
-
2. **Run the script**: `pnpm build:spotillustration`
|
|
11
|
-
3. **Done!** Your component is ready to use
|
|
12
|
-
|
|
13
|
-
The script automatically generates:
|
|
14
|
-
|
|
15
|
-
- Optimized JSON data file
|
|
16
|
-
- React TypeScript component
|
|
17
|
-
- Updated exports in `index.tsx`
|
|
18
|
-
|
|
19
|
-
### Development Workflow
|
|
20
|
-
|
|
21
|
-
During development, the script runs in watch mode:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
pnpm dev
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Any SVG files added or modified in the `svgs/` directory will automatically trigger:
|
|
28
|
-
|
|
29
|
-
1. SVG optimization
|
|
30
|
-
2. Component generation
|
|
31
|
-
3. Export updates
|
|
32
|
-
|
|
33
|
-
No manual intervention required.
|
|
34
|
-
|
|
35
|
-
## Design Requirements
|
|
36
|
-
|
|
37
|
-
### Frame Size
|
|
38
|
-
|
|
39
|
-
Use a **48x48** frame in Figma (or maintain the same aspect ratio).
|
|
40
|
-
|
|
41
|
-
### Color Palette
|
|
42
|
-
|
|
43
|
-
Use these exact hex colors in your Figma design. The script categorizes paths by color:
|
|
44
|
-
|
|
45
|
-
| Color | Hex | Layer | Purpose |
|
|
46
|
-
| ----------- | --------- | ----------- | ---------------------------------------- |
|
|
47
|
-
| White | `#ffffff` | `outline` | White stroke outline (hidden by default) |
|
|
48
|
-
| Purple tint | `#E1D1FB` | `face` | Main fills, primary shapes |
|
|
49
|
-
| Orange tint | `#FFC3A8` | `detail` | Accent details, highlights |
|
|
50
|
-
| Blue | `#0091EB` | `shadow` | Shadow layers, depth |
|
|
51
|
-
| Light blue | `#C2DFFB` | `highlight` | Light highlights, shine |
|
|
52
|
-
| Near black | `#1A1F2B` | `stroke` | Outlines, strokes |
|
|
53
|
-
|
|
54
|
-
### Layer Ordering
|
|
55
|
-
|
|
56
|
-
SVG renders paths in order, so arrange layers intentionally:
|
|
57
|
-
|
|
58
|
-
1. Outline (if needed)
|
|
59
|
-
2. Shadows (background)
|
|
60
|
-
3. Face/main shapes
|
|
61
|
-
4. Details
|
|
62
|
-
5. Highlights (foreground)
|
|
63
|
-
6. Strokes (top layer)
|
|
64
|
-
|
|
65
|
-
The script preserves your exact path order from Figma.
|
|
66
|
-
|
|
67
|
-
## How the Script Works
|
|
68
|
-
|
|
69
|
-
### Build Script: `svg-to-spot-illustration.cjs`
|
|
70
|
-
|
|
71
|
-
Location: `packages/components/scripts/svg-to-spot-illustration.cjs`
|
|
72
|
-
|
|
73
|
-
#### Process Flow
|
|
74
|
-
|
|
75
|
-
1. **SVG Optimization** (SVGO)
|
|
76
|
-
- Removes unnecessary attributes
|
|
77
|
-
- Cleans up paths
|
|
78
|
-
- Removes dimensions (preserves viewBox)
|
|
79
|
-
- Skip with `--no-optimize` flag if needed
|
|
80
|
-
|
|
81
|
-
2. **Path Categorization**
|
|
82
|
-
- Reads semantic illustration tokens from `@oztix/roadie-core`
|
|
83
|
-
- Matches each path's fill/stroke color to closest token color
|
|
84
|
-
- Assigns semantic layer name (face, detail, shadow, etc.)
|
|
85
|
-
- Preserves exact path order from source SVG
|
|
86
|
-
|
|
87
|
-
3. **JSON Generation**
|
|
88
|
-
- Creates optimized JSON in `json/` directory
|
|
89
|
-
- Structure:
|
|
90
|
-
```json
|
|
91
|
-
{
|
|
92
|
-
"$comment": "Generated file - do not edit directly",
|
|
93
|
-
"viewBox": "0 0 48 48",
|
|
94
|
-
"paths": [{ "d": "M26.179...", "layer": "face" }]
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
4. **Component Generation**
|
|
99
|
-
- Converts `kebab-case.svg` → `PascalCase.tsx`
|
|
100
|
-
- Uses `createSpotIllustration` factory
|
|
101
|
-
- Adds TypeScript types
|
|
102
|
-
- Example output:
|
|
103
|
-
|
|
104
|
-
```tsx
|
|
105
|
-
// Generated file - do not edit directly
|
|
106
|
-
import { createSpotIllustration } from './createSpotIllustration'
|
|
107
|
-
import heartData from './json/heart.json'
|
|
108
|
-
|
|
109
|
-
export const Heart = createSpotIllustration('Heart', heartData)
|
|
110
|
-
export type HeartProps = React.ComponentPropsWithRef<typeof Heart>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
5. **Export Updates**
|
|
114
|
-
- Scans directory for all component files
|
|
115
|
-
- Updates `index.tsx` with alphabetically sorted exports
|
|
116
|
-
- Preserves base component exports (SpotIllustration, createSpotIllustration)
|
|
117
|
-
|
|
118
|
-
### Usage Modes
|
|
119
|
-
|
|
120
|
-
**Build all SVGs:**
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
pnpm build:spotillustration
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**Build specific SVG:**
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
pnpm build:spotillustration src/components/SpotIllustration/svgs/heart.svg
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Watch mode (auto-rebuild on changes):**
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
pnpm build:spotillustration --watch
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
**Skip optimization:**
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
pnpm build:spotillustration --no-optimize
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Color Matching Algorithm
|
|
145
|
-
|
|
146
|
-
The script uses Euclidean distance in RGB color space to match fills/strokes to semantic tokens:
|
|
147
|
-
|
|
148
|
-
1. Normalize colors (expand short hex, lowercase)
|
|
149
|
-
2. Calculate RGB distance to each token color
|
|
150
|
-
3. Assign to closest matching layer
|
|
151
|
-
4. Default to 'face' if distance > 100 (no close match)
|
|
152
|
-
5. Strokes (paths with stroke but no fill) → 'stroke' layer
|
|
153
|
-
|
|
154
|
-
## Component Architecture
|
|
155
|
-
|
|
156
|
-
### Base Component: `SpotIllustration`
|
|
157
|
-
|
|
158
|
-
Accepts illustration data and renders themed SVG:
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
<SpotIllustration
|
|
162
|
-
illustration={illustrationData}
|
|
163
|
-
boxSize='600'
|
|
164
|
-
outline={false}
|
|
165
|
-
/>
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Factory: `createSpotIllustration`
|
|
169
|
-
|
|
170
|
-
Creates pre-configured components from JSON data:
|
|
171
|
-
|
|
172
|
-
```tsx
|
|
173
|
-
export const Heart = createSpotIllustration('Heart', heartData)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
This returns a component with:
|
|
177
|
-
|
|
178
|
-
- Pre-loaded illustration data
|
|
179
|
-
- Forward ref support
|
|
180
|
-
- All SpotIllustration props except `illustration`
|
|
181
|
-
- TypeScript types
|
|
182
|
-
|
|
183
|
-
### Styling System
|
|
184
|
-
|
|
185
|
-
Components use PandaCSS with semantic tokens:
|
|
186
|
-
|
|
187
|
-
- `colorPalette` system for theming
|
|
188
|
-
- Data attributes for layer targeting: `data-part="face"`, `data-part="shadow"`, etc.
|
|
189
|
-
- `outline` variant to show/hide white outline
|
|
190
|
-
- All PandaCSS style props supported
|
|
191
|
-
|
|
192
|
-
## File Structure
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
SpotIllustration/
|
|
196
|
-
├── README.md # This file
|
|
197
|
-
├── SpotIllustration.tsx # Base component
|
|
198
|
-
├── createSpotIllustration.tsx # Factory function
|
|
199
|
-
├── index.tsx # Exports (auto-updated)
|
|
200
|
-
├── svgs/ # Source SVG files
|
|
201
|
-
│ ├── heart.svg
|
|
202
|
-
│ ├── high-five.svg
|
|
203
|
-
│ └── note-music.svg
|
|
204
|
-
├── json/ # Generated JSON (do not edit)
|
|
205
|
-
│ ├── heart.json
|
|
206
|
-
│ ├── high-five.json
|
|
207
|
-
│ └── note-music.json
|
|
208
|
-
├── Heart.tsx # Generated component (do not edit)
|
|
209
|
-
├── HighFive.tsx # Generated component (do not edit)
|
|
210
|
-
└── NoteMusic.tsx # Generated component (do not edit)
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Troubleshooting
|
|
214
|
-
|
|
215
|
-
### Colors Not Matching Correctly
|
|
216
|
-
|
|
217
|
-
- Verify you're using exact hex colors from the table above
|
|
218
|
-
- Check that fills/strokes are applied directly to paths (not groups)
|
|
219
|
-
- Try `--no-optimize` if SVGO is changing colors
|
|
220
|
-
|
|
221
|
-
### Wrong Layer Order
|
|
222
|
-
|
|
223
|
-
- Check layer order in Figma (bottom to top)
|
|
224
|
-
- SVG renders paths sequentially
|
|
225
|
-
- Reorder in Figma and re-export
|
|
226
|
-
|
|
227
|
-
### Component Not Exported
|
|
228
|
-
|
|
229
|
-
- Check that `index.tsx` was updated (should happen automatically)
|
|
230
|
-
- Run `pnpm build:spotillustration` again
|
|
231
|
-
- Verify component file was created in SpotIllustration directory
|
|
232
|
-
|
|
233
|
-
### Watch Mode Not Detecting Changes
|
|
234
|
-
|
|
235
|
-
- Ensure file is saved in `svgs/` directory with `.svg` extension
|
|
236
|
-
- Check terminal for error messages
|
|
237
|
-
- Restart watch mode if needed
|
|
238
|
-
|
|
239
|
-
## Advanced Usage
|
|
240
|
-
|
|
241
|
-
### Custom Illustrations at Runtime
|
|
242
|
-
|
|
243
|
-
Use the base component with custom JSON data:
|
|
244
|
-
|
|
245
|
-
```tsx
|
|
246
|
-
import { SpotIllustration } from '@oztix/roadie-components'
|
|
247
|
-
|
|
248
|
-
const customData = {
|
|
249
|
-
viewBox: "0 0 48 48",
|
|
250
|
-
paths: [
|
|
251
|
-
{ d: "M10,10 L30,30", layer: "stroke" }
|
|
252
|
-
]
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
<SpotIllustration illustration={customData} />
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Importing from Dedicated Export Path
|
|
259
|
-
|
|
260
|
-
```tsx
|
|
261
|
-
import { Heart, NoteMusic } from '@oztix/roadie-components/spot-illustrations'
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
This import path only includes SpotIllustration components, useful for tree-shaking.
|
|
265
|
-
|
|
266
|
-
## Contributing
|
|
267
|
-
|
|
268
|
-
When adding new illustrations:
|
|
269
|
-
|
|
270
|
-
1. Follow the design requirements above
|
|
271
|
-
2. Use descriptive, kebab-case filenames (e.g., `music-note.svg`, not `icon-1.svg`)
|
|
272
|
-
3. Test in both light and dark themes
|
|
273
|
-
4. Verify outline variant works if applicable
|
|
274
|
-
5. Run the build script and commit all generated files
|
|
275
|
-
|
|
276
|
-
## Related Files
|
|
277
|
-
|
|
278
|
-
- Script: `packages/components/scripts/svg-to-spot-illustration.cjs`
|
|
279
|
-
- Tokens: `packages/core/src/tokens/semantic-tokens.json`
|
|
280
|
-
- Documentation: `docs/src/app/components/spot-illustration/page.mdx`
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { Hand } from './Hand'
|
|
5
|
-
import { Heart } from './Heart'
|
|
6
|
-
import { Ticket } from './Ticket'
|
|
7
|
-
|
|
8
|
-
describe('SpotIllustration', () => {
|
|
9
|
-
describe('rendering', () => {
|
|
10
|
-
it('should render an illustration', () => {
|
|
11
|
-
const { container } = render(<Heart />)
|
|
12
|
-
const svg = container.querySelector('svg')
|
|
13
|
-
expect(svg).toBeInTheDocument()
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('should render with default viewBox', () => {
|
|
17
|
-
const { container } = render(<Heart />)
|
|
18
|
-
const svg = container.querySelector('svg')
|
|
19
|
-
expect(svg).toHaveAttribute('viewBox', '0 0 48 48')
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('should render all path layers', () => {
|
|
23
|
-
const { container } = render(<Heart />)
|
|
24
|
-
const paths = container.querySelectorAll('path')
|
|
25
|
-
expect(paths.length).toBeGreaterThan(0)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
describe('boxSize prop', () => {
|
|
30
|
-
it('should apply default boxSize of 600', () => {
|
|
31
|
-
const { container } = render(<Heart />)
|
|
32
|
-
const svg = container.querySelector('svg')
|
|
33
|
-
// Check for class name base (PandaCSS uses different format)
|
|
34
|
-
expect(svg?.classList.length).toBeGreaterThan(0)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should apply custom boxSize', () => {
|
|
38
|
-
const { container } = render(<Heart boxSize='800' />)
|
|
39
|
-
const svg = container.querySelector('svg')
|
|
40
|
-
expect(svg?.classList.length).toBeGreaterThan(0)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('should support numeric boxSize values', () => {
|
|
44
|
-
const { container } = render(<Heart boxSize='1200' />)
|
|
45
|
-
const svg = container.querySelector('svg')
|
|
46
|
-
expect(svg?.classList.length).toBeGreaterThan(0)
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
describe('outline prop', () => {
|
|
51
|
-
it('should render outline layer by default (outline=false)', () => {
|
|
52
|
-
const { container } = render(<Ticket />)
|
|
53
|
-
const paths = container.querySelectorAll('path')
|
|
54
|
-
const outlinePaths = Array.from(paths).filter(
|
|
55
|
-
(path) => (path as Element).getAttribute('data-part') === 'outline'
|
|
56
|
-
)
|
|
57
|
-
expect(outlinePaths.length).toBeGreaterThan(0)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should hide outline when outline=false (default)', () => {
|
|
61
|
-
const { container } = render(<Ticket outline={false} />)
|
|
62
|
-
const svg = container.querySelector('svg')
|
|
63
|
-
// The outline should have opacity: 0 via CSS
|
|
64
|
-
expect(svg).toBeInTheDocument()
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should show outline when outline=true', () => {
|
|
68
|
-
const { container } = render(<Ticket outline={true} />)
|
|
69
|
-
const svg = container.querySelector('svg')
|
|
70
|
-
// The outline should be visible via CSS
|
|
71
|
-
expect(svg).toBeInTheDocument()
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe('className prop', () => {
|
|
76
|
-
it('should accept custom className', () => {
|
|
77
|
-
const { container } = render(<Heart className='custom-class' />)
|
|
78
|
-
const svg = container.querySelector('svg')
|
|
79
|
-
// PandaCSS merges classes differently - just check element exists
|
|
80
|
-
expect(svg).toBeInTheDocument()
|
|
81
|
-
expect(svg?.classList.length).toBeGreaterThan(0)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('should merge custom className with default classes', () => {
|
|
85
|
-
const { container } = render(
|
|
86
|
-
<Heart className='custom-class' boxSize='400' />
|
|
87
|
-
)
|
|
88
|
-
const svg = container.querySelector('svg')
|
|
89
|
-
expect(svg).toBeInTheDocument()
|
|
90
|
-
expect(svg?.classList.length).toBeGreaterThan(0)
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
describe('ref forwarding', () => {
|
|
95
|
-
it('should forward ref to svg element', () => {
|
|
96
|
-
let svgRef: SVGSVGElement | null = null
|
|
97
|
-
render(
|
|
98
|
-
<Heart
|
|
99
|
-
ref={(el) => {
|
|
100
|
-
svgRef = el
|
|
101
|
-
}}
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
expect(svgRef).toBeInstanceOf(SVGSVGElement)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('should allow accessing svg properties via ref', () => {
|
|
108
|
-
let svgRef: SVGSVGElement | null = null
|
|
109
|
-
render(
|
|
110
|
-
<Heart
|
|
111
|
-
ref={(el) => {
|
|
112
|
-
svgRef = el
|
|
113
|
-
}}
|
|
114
|
-
/>
|
|
115
|
-
)
|
|
116
|
-
expect(svgRef).not.toBeNull()
|
|
117
|
-
expect(svgRef!.getAttribute('viewBox')).toBeDefined()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('different illustrations', () => {
|
|
122
|
-
it('should render Heart illustration', () => {
|
|
123
|
-
const { container } = render(<Heart />)
|
|
124
|
-
const svg = container.querySelector('svg')
|
|
125
|
-
expect(svg).toBeInTheDocument()
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('should render Ticket illustration', () => {
|
|
129
|
-
const { container } = render(<Ticket />)
|
|
130
|
-
const svg = container.querySelector('svg')
|
|
131
|
-
expect(svg).toBeInTheDocument()
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('should render Hand illustration', () => {
|
|
135
|
-
const { container } = render(<Hand />)
|
|
136
|
-
const svg = container.querySelector('svg')
|
|
137
|
-
expect(svg).toBeInTheDocument()
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('should render different number of paths for different illustrations', () => {
|
|
141
|
-
const { container: heartContainer } = render(<Heart />)
|
|
142
|
-
const { container: handContainer } = render(<Hand />)
|
|
143
|
-
|
|
144
|
-
const heartPaths = heartContainer.querySelectorAll('path').length
|
|
145
|
-
const handPaths = handContainer.querySelectorAll('path').length
|
|
146
|
-
|
|
147
|
-
// Heart and Hand have different numbers of paths
|
|
148
|
-
expect(heartPaths).not.toBe(handPaths)
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
describe('layer colors', () => {
|
|
153
|
-
it('should use semantic token colors via data-part attributes', () => {
|
|
154
|
-
const { container } = render(<Heart />)
|
|
155
|
-
const paths = container.querySelectorAll('path')
|
|
156
|
-
|
|
157
|
-
// Check that paths have data-part attributes for styling
|
|
158
|
-
const dataParts = Array.from(paths)
|
|
159
|
-
.map((path) => (path as Element).getAttribute('data-part'))
|
|
160
|
-
.filter(Boolean)
|
|
161
|
-
|
|
162
|
-
expect(dataParts.length).toBeGreaterThan(0)
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
describe('accessibility', () => {
|
|
167
|
-
it('should be decorative by default (no role)', () => {
|
|
168
|
-
const { container } = render(<Heart />)
|
|
169
|
-
const svg = container.querySelector('svg')
|
|
170
|
-
expect(svg).not.toHaveAttribute('role')
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('should accept aria attributes', () => {
|
|
174
|
-
const { container } = render(<Heart aria-label='Heart icon' />)
|
|
175
|
-
const svg = container.querySelector('svg')
|
|
176
|
-
expect(svg).toHaveAttribute('aria-label', 'Heart icon')
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
})
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { forwardRef } from 'react'
|
|
2
|
-
|
|
3
|
-
import { styled } from '@oztix/roadie-core/jsx'
|
|
4
|
-
import type { HTMLStyledProps } from '@oztix/roadie-core/jsx'
|
|
5
|
-
|
|
6
|
-
interface PathData {
|
|
7
|
-
d: string
|
|
8
|
-
layer: 'outline' | 'face' | 'detail' | 'shadow' | 'highlight' | 'stroke'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface IllustrationData {
|
|
12
|
-
viewBox: string
|
|
13
|
-
paths: PathData[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const StyledSvg = styled('svg', {
|
|
17
|
-
base: {
|
|
18
|
-
boxSize: '600',
|
|
19
|
-
fill: 'none',
|
|
20
|
-
'& [data-part="outline"]': {
|
|
21
|
-
fill: 'illustrations.outline'
|
|
22
|
-
},
|
|
23
|
-
'& [data-part="face"]': {
|
|
24
|
-
fill: 'illustrations.face'
|
|
25
|
-
},
|
|
26
|
-
'& [data-part="detail"]': {
|
|
27
|
-
fill: 'illustrations.detail'
|
|
28
|
-
},
|
|
29
|
-
'& [data-part="shadow"]': {
|
|
30
|
-
fill: 'illustrations.shadow'
|
|
31
|
-
},
|
|
32
|
-
'& [data-part="highlight"]': {
|
|
33
|
-
fill: 'illustrations.highlight'
|
|
34
|
-
},
|
|
35
|
-
'& [data-part="stroke"]': {
|
|
36
|
-
stroke: 'illustrations.stroke',
|
|
37
|
-
strokeWidth: '0.5',
|
|
38
|
-
strokeLinecap: 'round',
|
|
39
|
-
strokeLinejoin: 'round',
|
|
40
|
-
fill: 'none'
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
variants: {
|
|
44
|
-
outline: {
|
|
45
|
-
true: {
|
|
46
|
-
'& [data-part="outline"]': {
|
|
47
|
-
opacity: 1
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
false: {
|
|
51
|
-
'& [data-part="outline"]': {
|
|
52
|
-
opacity: 0
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
defaultVariants: {
|
|
58
|
-
outline: false
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
export interface SpotIllustrationProps
|
|
63
|
-
extends Omit<HTMLStyledProps<'svg'>, 'outline'> {
|
|
64
|
-
/**
|
|
65
|
-
* Illustration definition containing viewBox and paths
|
|
66
|
-
*/
|
|
67
|
-
illustration: IllustrationData
|
|
68
|
-
/**
|
|
69
|
-
* Whether to show the outline layer
|
|
70
|
-
* @default false
|
|
71
|
-
*/
|
|
72
|
-
outline?: boolean
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const SpotIllustration = forwardRef<
|
|
76
|
-
SVGSVGElement,
|
|
77
|
-
SpotIllustrationProps
|
|
78
|
-
>(({ illustration, ...props }, ref) => {
|
|
79
|
-
// Fallback to default viewBox if not provided
|
|
80
|
-
const { viewBox = '0 0 48 48', paths } = illustration
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<StyledSvg
|
|
84
|
-
ref={ref}
|
|
85
|
-
xmlns='http://www.w3.org/2000/svg'
|
|
86
|
-
viewBox={viewBox}
|
|
87
|
-
{...props}
|
|
88
|
-
>
|
|
89
|
-
{paths.map((path: PathData, index: number) => (
|
|
90
|
-
<path key={index} d={path.d} data-part={path.layer} />
|
|
91
|
-
))}
|
|
92
|
-
</StyledSvg>
|
|
93
|
-
)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
SpotIllustration.displayName = 'SpotIllustration'
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// Generated file - do not edit directly
|
|
2
|
-
import { createSpotIllustration } from './createSpotIllustration'
|
|
3
|
-
import ticketData from './json/ticket.json'
|
|
4
|
-
|
|
5
|
-
export const Ticket = createSpotIllustration('Ticket', ticketData)
|
|
6
|
-
export type TicketProps = React.ComponentPropsWithRef<typeof Ticket>
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// Generated file - do not edit directly
|
|
2
|
-
import { createSpotIllustration } from './createSpotIllustration'
|
|
3
|
-
import wineglassData from './json/wine-glass.json'
|
|
4
|
-
|
|
5
|
-
export const WineGlass = createSpotIllustration('WineGlass', wineglassData)
|
|
6
|
-
export type WineGlassProps = React.ComponentPropsWithRef<typeof WineGlass>
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { forwardRef } from 'react'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
SpotIllustration,
|
|
5
|
-
type SpotIllustrationProps
|
|
6
|
-
} from './SpotIllustration'
|
|
7
|
-
|
|
8
|
-
type IllustrationJson = SpotIllustrationProps['illustration']
|
|
9
|
-
|
|
10
|
-
export type SpotIllustrationComponentProps = Omit<
|
|
11
|
-
SpotIllustrationProps,
|
|
12
|
-
'illustration'
|
|
13
|
-
>
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Factory function to create a SpotIllustration component from JSON data
|
|
17
|
-
*
|
|
18
|
-
* @param name - Display name for the component
|
|
19
|
-
* @param data - JSON data imported from the json folder
|
|
20
|
-
* @returns A React component that renders the spot illustration
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```tsx
|
|
24
|
-
* import { createSpotIllustration } from './createSpotIllustration'
|
|
25
|
-
* import noteMusicData from './json/note-music.json'
|
|
26
|
-
*
|
|
27
|
-
* export const NoteMusic = createSpotIllustration('NoteMusic', noteMusicData)
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export function createSpotIllustration(name: string, data: unknown) {
|
|
31
|
-
const Component = forwardRef<SVGSVGElement, SpotIllustrationComponentProps>(
|
|
32
|
-
(props, ref) => {
|
|
33
|
-
return (
|
|
34
|
-
<SpotIllustration
|
|
35
|
-
ref={ref}
|
|
36
|
-
illustration={data as IllustrationJson}
|
|
37
|
-
{...props}
|
|
38
|
-
/>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
Component.displayName = name
|
|
44
|
-
|
|
45
|
-
return Component
|
|
46
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export { SpotIllustration } from './SpotIllustration'
|
|
2
|
-
export type { SpotIllustrationProps } from './SpotIllustration'
|
|
3
|
-
|
|
4
|
-
export { createSpotIllustration } from './createSpotIllustration'
|
|
5
|
-
export type { SpotIllustrationComponentProps } from './createSpotIllustration'
|
|
6
|
-
|
|
7
|
-
// Generated exports - do not edit manually
|
|
8
|
-
export { ArrowUpRight } from './ArrowUpRight'
|
|
9
|
-
export type { ArrowUpRightProps } from './ArrowUpRight'
|
|
10
|
-
|
|
11
|
-
export { CowboyHat } from './CowboyHat'
|
|
12
|
-
export type { CowboyHatProps } from './CowboyHat'
|
|
13
|
-
|
|
14
|
-
export { Cursor } from './Cursor'
|
|
15
|
-
export type { CursorProps } from './Cursor'
|
|
16
|
-
|
|
17
|
-
export { FlowerSpiral } from './FlowerSpiral'
|
|
18
|
-
export type { FlowerSpiralProps } from './FlowerSpiral'
|
|
19
|
-
|
|
20
|
-
export { Football } from './Football'
|
|
21
|
-
export type { FootballProps } from './Football'
|
|
22
|
-
|
|
23
|
-
export { Hand } from './Hand'
|
|
24
|
-
export type { HandProps } from './Hand'
|
|
25
|
-
|
|
26
|
-
export { Heart } from './Heart'
|
|
27
|
-
export type { HeartProps } from './Heart'
|
|
28
|
-
|
|
29
|
-
export { HighFive } from './HighFive'
|
|
30
|
-
export type { HighFiveProps } from './HighFive'
|
|
31
|
-
|
|
32
|
-
export { MapPin } from './MapPin'
|
|
33
|
-
export type { MapPinProps } from './MapPin'
|
|
34
|
-
|
|
35
|
-
export { NoteMusic } from './NoteMusic'
|
|
36
|
-
export type { NoteMusicProps } from './NoteMusic'
|
|
37
|
-
|
|
38
|
-
export { Ticket } from './Ticket'
|
|
39
|
-
export type { TicketProps } from './Ticket'
|
|
40
|
-
|
|
41
|
-
export { WineGlass } from './WineGlass'
|
|
42
|
-
export type { WineGlassProps } from './WineGlass'
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$comment": "Generated file - do not edit directly",
|
|
3
|
-
"viewBox": "0 0 48 48",
|
|
4
|
-
"paths": [
|
|
5
|
-
{
|
|
6
|
-
"d": "m40.531.849 6.605 4.69c.574.396.906 1.083.857 1.782L46.694 25.9c-.117 1.677-2.22 2.462-3.409 1.275l-3.032-3.031-8.035 8.033-.413 4.054a2 2 0 0 1-.575 1.211l-8.488 8.485a2.026 2.026 0 0 1-2.695.2l-6.541-4.64a2 2 0 0 1-.703-.897l-2.287-5.8-2.626-1.035a2 2 0 0 1-.424-.23l-6.624-4.7c-.995-.707-1.12-2.183-.257-3.046l8.657-8.646a2 2 0 0 1 1.084-.46l4.051-.413 8.527-8.529-3.267-2.319a2.001 2.001 0 0 1 1.018-3.626l18.578-1.3c.466-.014.913.09 1.298.364",
|
|
7
|
-
"layer": "outline"
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"d": "m17.153 23.364-8.53 8.53-6.624-4.702 8.53-8.53 3.312 2.351z",
|
|
11
|
-
"layer": "detail"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"d": "m27.417 8.481 4.446 4.446-9.95 9.949.001.001h-.002l-4.759.487-6.624-4.702 4.76-.486L25.993 7.47z",
|
|
15
|
-
"layer": "detail"
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"d": "M45.998 7.183 27.42 8.479 20.795 3.78l18.578-1.3z",
|
|
19
|
-
"layer": "detail"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"d": "M45.998 7.182 27.419 8.48l4.446 4.447-9.95 9.95-4.762.487-8.53 8.53 9.082 3.58 3.58 9.083 8.53-8.53.487-4.762 9.951-9.95 4.446 4.445z",
|
|
23
|
-
"layer": "face"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"d": "m12.058 33.247 5.65 2.227 3.58 9.082-6.625-4.701z",
|
|
27
|
-
"layer": "shadow"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"d": "M45.998 7.182 27.419 8.48m18.579-1.298L44.699 25.76l-4.446-4.445-9.951 9.95-.487 4.762-8.53 8.53-3.58-9.083-9.082-3.58 8.53-8.53M45.998 7.182 39.373 2.48l-18.578 1.3 6.624 4.7m0 0 4.446 4.447-9.95 9.95m0 0-4.762.487m4.762-.487-6.626-4.701-4.76.487 6.624 4.701m-5.095 9.883 5.65 2.227 3.58 9.082-6.625-4.701zm-3.434-1.351 8.53-8.53-6.624-4.702L2 27.194zm23.239-18.969-4.446-4.446-1.424-1.01-10.705 10.706 6.624 4.701 9.951-9.95z",
|
|
31
|
-
"layer": "stroke"
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|