@imj_media/ui 1.10.6 → 1.10.8
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/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/catalog/design-index.json +114 -16
- package/catalog/design-index.schema.json +40 -3
- package/dist/index.css +1 -1
- package/dist/index.esm.js +6421 -6228
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +25 -25
- package/dist/index.js.map +1 -1
- package/dist/modules/Modal/Modal.d.ts.map +1 -1
- package/dist/modules/Modal/components/molecules/ModalBody.d.ts.map +1 -1
- package/dist/modules/Modal/context/ModalContext.d.ts.map +1 -1
- package/dist/modules/Modal/stories/Modal.stories.d.ts +13 -0
- package/dist/modules/Modal/stories/Modal.stories.d.ts.map +1 -1
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/shared/types/modal.d.ts +10 -1
- package/dist/shared/types/modal.d.ts.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/lang/es/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.10.8] - 2026-05-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`Tabs`:** reexportación en el barrel público (`src/modules/index.ts`) para consumo desde `@imj_media/ui` en runtime y tipos del paquete npm.
|
|
15
|
+
- **Release:** script `validate:public-exports` (módulos con stories deben estar en el barrel; excepciones en `public-export-exceptions.json`) integrado en `validate:publish` y `release:gate`.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **`DEVELOPMENT_GUIDE.md`:** guía de exportación pública y verificación antes de release.
|
|
20
|
+
|
|
21
|
+
### Documentation
|
|
22
|
+
|
|
23
|
+
- **Monorepo:** regla Cursor `imj-ui-obligations-public-export` y referencias en skill `imj-ui-package`.
|
|
24
|
+
|
|
25
|
+
## [1.10.7] - 2026-05-26
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`Modal`:** nueva prop `scrollMode` (`'paper'` | `'scroll'`). El modo `paper` (por defecto) fija el panel a la altura del viewport y delega el scroll al `Modal.Body`; el modo `scroll` mantiene el comportamiento clásico de scroll externo en el overlay.
|
|
30
|
+
- **Catálogo:** templates de uso y recetas de composición por componente en `design-index.json`.
|
|
31
|
+
|
|
10
32
|
## [1.10.6] - 2026-05-25
|
|
11
33
|
|
|
12
34
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Biblioteca de componentes UI moderna y accesible para React, construida con TypeScript y Tailwind CSS.
|
|
6
6
|
|
|
7
|
-
> **Versión publicada:** `1.10.
|
|
7
|
+
> **Versión publicada:** `1.10.8` — actualizar este número y `CHANGELOG.md` antes de cada release (orden y scripts: regla **`imj-ui-obligations-release`**; atajo `npm run publish:patch|minor|major` o pasos `bump:*` → `release:git` → `release:publish`).
|
|
8
8
|
|
|
9
9
|
## 📦 Instalación
|
|
10
10
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
3
|
"package": "@imj_media/ui",
|
|
4
|
-
"version": "1.10.
|
|
5
|
-
"indexedAt": "2026-05-
|
|
4
|
+
"version": "1.10.8",
|
|
5
|
+
"indexedAt": "2026-05-27T20:27:20.532Z",
|
|
6
6
|
"orbitTokensVersion": "1.3.1",
|
|
7
7
|
"modules": [
|
|
8
8
|
{
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"Accordion.stories.tsx:Default"
|
|
45
45
|
],
|
|
46
46
|
"snippet": "<Accordion\n id=\"section-1\"\n title=\"Sección\"\n subtitle=\"Opcional\"\n defaultOpen={false}\n>\n Contenido colapsable\n</Accordion>"
|
|
47
|
-
}
|
|
47
|
+
},
|
|
48
|
+
"standaloneSnippet": "import React, { useState } from 'react';\n\nexport function Accordion({\n items,\n defaultOpen,\n}: {\n items: { id: string; title: string; content: React.ReactNode }[];\n defaultOpen?: string;\n}) {\n const [openId, setOpenId] = useState<string | undefined>(defaultOpen);\n\n const toggle = (id: string) => {\n setOpenId((prev) => (prev === id ? undefined : id));\n };\n\n return (\n <div\n style={{\n border: '1px solid rgb(199, 199, 204)',\n borderRadius: '8px',\n overflow: 'hidden',\n }}\n >\n {items.map((item, index) => {\n const isOpen = openId === item.id;\n return (\n <div key={item.id}>\n {index > 0 && (\n <div style={{ height: '1px', backgroundColor: 'rgb(199, 199, 204)' }} />\n )}\n <button\n onClick={() => toggle(item.id)}\n style={{\n width: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '12px 4px',\n backgroundColor: 'rgb(255, 255, 255)',\n border: 'none',\n cursor: 'pointer',\n fontSize: '16px',\n fontWeight: '500',\n color: 'rgb(48, 51, 54)',\n fontFamily: 'inherit',\n textAlign: 'left',\n }}\n >\n <span>{item.title}</span>\n <span\n style={{\n transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',\n transition: 'transform 200ms',\n fontSize: '12px',\n color: 'rgb(89, 89, 94)',\n }}\n >\n ▼\n </span>\n </button>\n <div\n style={{\n display: isOpen ? 'block' : 'none',\n padding: '12px 4px',\n backgroundColor: 'rgb(247, 247, 250)',\n color: 'rgb(48, 51, 54)',\n fontSize: '14px',\n lineHeight: '1.5',\n }}\n >\n {item.content}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"
|
|
48
49
|
},
|
|
49
50
|
{
|
|
50
51
|
"id": "Alert",
|
|
@@ -69,7 +70,8 @@
|
|
|
69
70
|
"<Alert color=\"success\" variant=\"contained\" message=\"Variante contained (por defecto)\" title=\"Contained\" open={true} />\n <Alert color=\"info\" variant=\"contained\" message=\"Variante contained (por defecto)\" title=\"Contained\" open={true} />\n <Alert color=\"warning\" variant=\"contained\" message=\"Variante contained (por defecto)\" title=\"Contained\" open={true} />\n <Alert color=\"danger\" variant=\"contained\" message=\"Variante contained (por defecto)\" title=\"Contained\" open={true} />\n <Alert color=\"success\" variant=\"outlined\" message=\"Variante outlined con borde\" title=\"Outlined\" open={true} />\n <Alert color=\"info\" variant=\"outlined\" message=\"Variante outlined con borde\" title=\"Outlined\" open={true} />\n <Alert color=\"warning\" variant=\"outlined\" message=\"Variante outlined con borde\" title=\"Outlined\" open={true} />\n <Alert color=\"danger\" variant=\"outlined\" message=\"Variante outlined con borde\" title=\"Outlined\" open={true} />"
|
|
70
71
|
]
|
|
71
72
|
}
|
|
72
|
-
]
|
|
73
|
+
],
|
|
74
|
+
"standaloneSnippet": "import React, { useState } from 'react';\n\nconst variantMap = {\n info: { border: 'rgb(5, 181, 212)', bg: 'rgba(0, 0, 0, 0.08)', text: 'rgb(5, 181, 212)' },\n success: { border: 'rgb(33, 196, 94)', bg: 'rgba(0, 0, 0, 0.08)', text: 'rgb(33, 196, 94)' },\n warning: { border: 'rgb(235, 179, 8)', bg: 'rgba(0, 0, 0, 0.08)', text: 'rgb(235, 179, 8)' },\n error: { border: 'rgb(240, 69, 69)', bg: 'rgba(0, 0, 0, 0.08)', text: 'rgb(240, 69, 69)' },\n};\n\nexport function Alert({\n children,\n variant = 'info',\n title,\n closable = false,\n onClose,\n}: {\n children: React.ReactNode;\n variant?: keyof typeof variantMap;\n title?: string;\n closable?: boolean;\n onClose?: () => void;\n}) {\n const [visible, setVisible] = useState(true);\n if (!visible) return null;\n\n const v = variantMap[variant];\n\n const handleClose = () => {\n setVisible(false);\n onClose?.();\n };\n\n return (\n <div\n role=\"alert\"\n style={{\n position: 'relative',\n padding: '12px 4px',\n borderLeft: `4px solid ${v.border}`,\n backgroundColor: v.bg,\n borderRadius: '8px',\n color: 'rgb(48, 51, 54)',\n fontSize: '14px',\n lineHeight: '1.5',\n }}\n >\n {closable && (\n <button\n onClick={handleClose}\n aria-label=\"Close\"\n style={{\n position: 'absolute',\n top: '2px',\n right: '2px',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: '16px',\n color: 'rgb(89, 89, 94)',\n padding: '4px',\n lineHeight: '1',\n }}\n >\n ×\n </button>\n )}\n {title && (\n <div style={{ fontWeight: '700', marginBottom: '4px', color: v.text }}>\n {title}\n </div>\n )}\n <div>{children}</div>\n </div>\n );\n}\n"
|
|
73
75
|
},
|
|
74
76
|
{
|
|
75
77
|
"id": "AlertDialog",
|
|
@@ -113,7 +115,8 @@
|
|
|
113
115
|
},
|
|
114
116
|
"examples": []
|
|
115
117
|
}
|
|
116
|
-
]
|
|
118
|
+
],
|
|
119
|
+
"standaloneSnippet": "import React, { useState } from 'react';\n\nconst sizeMap = {\n sm: { dimension: '32px', fontSize: '12px' },\n md: { dimension: '40px', fontSize: '14px' },\n lg: { dimension: '56px', fontSize: '18px' },\n xl: { dimension: '80px', fontSize: '20px' },\n};\n\nexport function Avatar({\n src,\n alt = '',\n size = 'md',\n fallback,\n}: {\n src?: string;\n alt?: string;\n size?: keyof typeof sizeMap;\n fallback?: string;\n}) {\n const [imgError, setImgError] = useState(false);\n const s = sizeMap[size];\n const showImage = src && !imgError;\n\n const baseStyle: React.CSSProperties = {\n width: s.dimension,\n height: s.dimension,\n borderRadius: '9999px',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n flexShrink: 0,\n };\n\n if (showImage) {\n return (\n <img\n src={src}\n alt={alt}\n onError={() => setImgError(true)}\n style={{\n ...baseStyle,\n objectFit: 'cover',\n }}\n />\n );\n }\n\n return (\n <div\n style={{\n ...baseStyle,\n backgroundColor: 'rgb(247, 247, 250)',\n color: 'rgb(48, 51, 54)',\n fontSize: s.fontSize,\n fontWeight: '500',\n userSelect: 'none',\n }}\n >\n {fallback || '?'}\n </div>\n );\n}\n"
|
|
117
120
|
},
|
|
118
121
|
{
|
|
119
122
|
"id": "Badge",
|
|
@@ -139,7 +142,8 @@
|
|
|
139
142
|
"import { Badge } from '@/modules/Badge';\n\nexport default function Example() {\n return (\n <>\n <Badge appearance={{ size: 'sm', color: 'accent' }} text={{ label: '5' }} />\n <Badge appearance={{ size: 'sm', color: 'gray' }} text={{ label: '5' }} />\n <Badge appearance={{ size: 'sm', color: 'success' }} text={{ label: '5' }} />\n <Badge appearance={{ size: 'sm', color: 'warning' }} text={{ label: '5' }} />\n <Badge appearance={{ size: 'sm', color: 'danger' }} text={{ label: '5' }} />\n <Badge appearance={{ size: 'sm', color: 'info' }} text={{ label: '5' }} />\n <Badge appearance={{ size: 'sm' }} text={{ label: '5' }} state={{ disabled: true }} />\n </>\n )\n}"
|
|
140
143
|
]
|
|
141
144
|
}
|
|
142
|
-
]
|
|
145
|
+
],
|
|
146
|
+
"standaloneSnippet": "import React from 'react';\n\nconst colorMap = {\n primary: { solid: 'rgb(54, 89, 194)', text: 'rgb(54, 89, 194)' },\n secondary: { solid: 'rgb(240, 69, 69)', text: 'rgb(240, 69, 69)' },\n tertiary: { solid: 'rgb(33, 196, 94)', text: 'rgb(33, 196, 94)' },\n destructive: { solid: 'rgb(235, 179, 8)', text: 'rgb(235, 179, 8)' },\n neutral: { solid: 'rgb(89, 89, 94)', text: 'rgb(89, 89, 94)' },\n};\n\nconst sizeMap = {\n sm: { padding: '2px 6px', fontSize: '12px' },\n md: { padding: '4px 2px', fontSize: '12px' },\n lg: { padding: '4px 12px', fontSize: '14px' },\n};\n\nfunction hexToRgba(hex: string, alpha: number): string {\n const h = hex.replace('#', '');\n const n = parseInt(h, 16);\n return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;\n}\n\nexport function Badge({\n children,\n color = 'primary',\n size = 'md',\n theme = 'soft',\n}: {\n children: React.ReactNode;\n color?: keyof typeof colorMap;\n size?: keyof typeof sizeMap;\n theme?: 'soft' | 'solid' | 'outlined';\n}) {\n const c = colorMap[color];\n const s = sizeMap[size];\n\n const styles: Record<string, string | number> = {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: s.padding,\n fontSize: s.fontSize,\n fontWeight: '500',\n lineHeight: '1.25',\n borderRadius: '9999px',\n whiteSpace: 'nowrap',\n };\n\n if (theme === 'solid') {\n styles.backgroundColor = c.solid;\n styles.color = 'rgb(255, 255, 255)';\n styles.border = 'none';\n } else if (theme === 'outlined') {\n styles.backgroundColor = 'transparent';\n styles.color = c.text;\n styles.border = `1px solid ${c.solid}`;\n } else {\n styles.backgroundColor = hexToRgba(c.solid, 0.1);\n styles.color = c.text;\n styles.border = 'none';\n }\n\n return <span style={styles}>{children}</span>;\n}\n"
|
|
143
147
|
},
|
|
144
148
|
{
|
|
145
149
|
"id": "Badges",
|
|
@@ -213,7 +217,8 @@
|
|
|
213
217
|
"import { Button } from '@imj_media/ui';\n\n export default function Example() {\n return (\n <>\n <Button color=\"primary\" tooltip=\"Tooltip\">Blue</Button>\n <Button color=\"secondary\" tooltip=\"Tooltip\">Red</Button>\n <Button color=\"tertiary\" tooltip=\"Tooltip\">Green</Button>\n <Button color=\"destructive\" tooltip=\"Tooltip\">Orange</Button>\n </>\n )\n }"
|
|
214
218
|
]
|
|
215
219
|
}
|
|
216
|
-
]
|
|
220
|
+
],
|
|
221
|
+
"standaloneSnippet": "import React from 'react';\n\nconst colorMap = {\n primary: {\n solid: { bg: 'rgb(54, 89, 194)', text: 'rgb(255, 255, 255)', hover: 'rgb(51, 82, 176)', pressed: 'rgb(46, 74, 161)', border: 'transparent' },\n outlined: { bg: 'transparent', text: 'rgb(54, 89, 194)', hover: 'rgb(51, 82, 176)', pressed: 'rgb(46, 74, 161)', border: 'rgb(54, 89, 194)' },\n text: { bg: 'transparent', text: 'rgb(54, 89, 194)', hover: 'rgb(51, 82, 176)', pressed: 'rgb(46, 74, 161)', border: 'transparent' },\n ghost: { bg: 'transparent', text: 'rgb(54, 89, 194)', hover: 'rgba(0,0,0,0.04)', pressed: 'rgba(0,0,0,0.08)', border: 'transparent' },\n },\n secondary: {\n solid: { bg: 'rgb(240, 69, 69)', text: 'rgb(255, 255, 255)', hover: 'rgb(212, 61, 61)', pressed: 'rgb(184, 51, 51)', border: 'transparent' },\n outlined: { bg: 'transparent', text: 'rgb(240, 69, 69)', hover: 'rgb(212, 61, 61)', pressed: 'rgb(184, 51, 51)', border: 'rgb(240, 69, 69)' },\n text: { bg: 'transparent', text: 'rgb(240, 69, 69)', hover: 'rgb(212, 61, 61)', pressed: 'rgb(184, 51, 51)', border: 'transparent' },\n ghost: { bg: 'transparent', text: 'rgb(240, 69, 69)', hover: 'rgba(0,0,0,0.04)', pressed: 'rgba(0,0,0,0.08)', border: 'transparent' },\n },\n tertiary: {\n solid: { bg: 'rgb(33, 196, 94)', text: 'rgb(255, 255, 255)', hover: 'rgb(31, 181, 87)', pressed: 'rgb(28, 163, 79)', border: 'transparent' },\n outlined: { bg: 'transparent', text: 'rgb(33, 196, 94)', hover: 'rgb(31, 181, 87)', pressed: 'rgb(28, 163, 79)', border: 'rgb(33, 196, 94)' },\n text: { bg: 'transparent', text: 'rgb(33, 196, 94)', hover: 'rgb(31, 181, 87)', pressed: 'rgb(28, 163, 79)', border: 'transparent' },\n ghost: { bg: 'transparent', text: 'rgb(33, 196, 94)', hover: 'rgba(0,0,0,0.04)', pressed: 'rgba(0,0,0,0.08)', border: 'transparent' },\n },\n destructive: {\n solid: { bg: 'rgb(235, 179, 8)', text: 'rgb(255, 255, 255)', hover: 'rgb(209, 158, 8)', pressed: 'rgb(181, 140, 5)', border: 'transparent' },\n outlined: { bg: 'transparent', text: 'rgb(235, 179, 8)', hover: 'rgb(209, 158, 8)', pressed: 'rgb(181, 140, 5)', border: 'rgb(235, 179, 8)' },\n text: { bg: 'transparent', text: 'rgb(235, 179, 8)', hover: 'rgb(209, 158, 8)', pressed: 'rgb(181, 140, 5)', border: 'transparent' },\n ghost: { bg: 'transparent', text: 'rgb(235, 179, 8)', hover: 'rgba(0,0,0,0.04)', pressed: 'rgba(0,0,0,0.08)', border: 'transparent' },\n },\n};\n\nconst sizeMap = {\n xs: { padding: '4px 2px', fontSize: '12px' },\n sm: { padding: '2px 12px', fontSize: '14px' },\n md: { padding: '10px 4px', fontSize: '16px' },\n lg: { padding: '12px 6px', fontSize: '18px' },\n};\n\nexport function Button({\n children,\n color = 'primary',\n size = 'sm',\n theme = 'solid',\n disabled = false,\n fullWidth = false,\n rounded = false,\n onClick,\n}: {\n children: React.ReactNode;\n color?: keyof typeof colorMap;\n size?: keyof typeof sizeMap;\n theme?: 'solid' | 'outlined' | 'text' | 'ghost';\n disabled?: boolean;\n fullWidth?: boolean;\n rounded?: boolean;\n onClick?: () => void;\n}) {\n const c = colorMap[color][theme];\n const s = sizeMap[size];\n const [hovered, setHovered] = React.useState(false);\n const [pressed, setPressed] = React.useState(false);\n\n const bgColor = disabled ? c.bg : pressed ? (theme === 'solid' ? c.pressed : 'rgba(0,0,0,0.06)') : hovered ? (theme === 'solid' ? c.hover : 'rgba(0,0,0,0.03)') : c.bg;\n const textColor = disabled ? c.text : hovered && theme !== 'solid' ? c.hover : c.text;\n\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => { setHovered(false); setPressed(false); }}\n onMouseDown={() => setPressed(true)}\n onMouseUp={() => setPressed(false)}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: s.padding,\n fontSize: s.fontSize,\n fontWeight: '600',\n fontFamily: 'inherit',\n lineHeight: '1.25',\n borderRadius: rounded ? '9999px' : '8px',\n border: theme === 'outlined' ? `1px solid ${c.border}` : 'none',\n backgroundColor: bgColor,\n color: textColor,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n width: fullWidth ? '100%' : 'auto',\n transition: 'background-color 150ms, color 150ms',\n boxSizing: 'border-box',\n }}\n >\n {children}\n </button>\n );\n}\n"
|
|
217
222
|
},
|
|
218
223
|
{
|
|
219
224
|
"id": "ButtonGroup",
|
|
@@ -1128,7 +1133,8 @@
|
|
|
1128
1133
|
"<Input \n label=\"Input Deshabilitado\"\n disabled={true}\n value=\"Valor no editable\"\n />"
|
|
1129
1134
|
]
|
|
1130
1135
|
}
|
|
1131
|
-
]
|
|
1136
|
+
],
|
|
1137
|
+
"standaloneSnippet": "import React from 'react';\n\nconst sizeMap = {\n sm: { padding: '6px 2px', fontSize: '14px' },\n md: { padding: '2px 12px', fontSize: '16px' },\n lg: { padding: '12px 4px', fontSize: '18px' },\n};\n\nexport function Input({\n value,\n onChange,\n placeholder,\n disabled = false,\n size = 'md',\n error,\n type = 'text',\n}: {\n value?: string;\n onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;\n placeholder?: string;\n disabled?: boolean;\n size?: keyof typeof sizeMap;\n error?: string;\n type?: 'text' | 'password' | 'email' | 'number';\n}) {\n const s = sizeMap[size];\n const [focused, setFocused] = React.useState(false);\n\n const borderColor = error\n ? 'rgb(240, 69, 69)'\n : focused\n ? 'rgb(54, 89, 194)'\n : 'rgb(199, 199, 204)';\n\n const input = (\n <input\n type={type}\n value={value}\n onChange={onChange}\n placeholder={placeholder}\n disabled={disabled}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n style={{\n width: '100%',\n padding: s.padding,\n fontSize: s.fontSize,\n fontFamily: 'inherit',\n lineHeight: '1.5',\n color: 'rgb(48, 51, 54)',\n backgroundColor: 'rgb(255, 255, 255)',\n border: `1px solid ${borderColor}`,\n borderRadius: '8px',\n outline: 'none',\n opacity: disabled ? 0.5 : 1,\n cursor: disabled ? 'not-allowed' : 'text',\n transition: 'border-color 150ms',\n boxSizing: 'border-box',\n }}\n />\n );\n\n if (error) {\n return (\n <div>\n {input}\n <div\n style={{\n marginTop: '4px',\n fontSize: '12px',\n color: 'rgb(240, 69, 69)',\n }}\n >\n {error}\n </div>\n </div>\n );\n }\n\n return input;\n}\n"
|
|
1132
1138
|
},
|
|
1133
1139
|
{
|
|
1134
1140
|
"id": "LegacyButton",
|
|
@@ -1436,7 +1442,8 @@
|
|
|
1436
1442
|
"Modal.stories.tsx:WithTabs"
|
|
1437
1443
|
],
|
|
1438
1444
|
"snippet": "<Modal isOpen={open} onClose={() => setOpen(false)} title=\"Título\">\n <Modal.Body>Contenido</Modal.Body>\n <Modal.Footer>\n <Button onClick={() => setOpen(false)}>Cerrar</Button>\n </Modal.Footer>\n</Modal>"
|
|
1439
|
-
}
|
|
1445
|
+
},
|
|
1446
|
+
"standaloneSnippet": "import React from 'react';\n\nconst sizeMap = {\n sm: '400px',\n md: '560px',\n lg: '720px',\n};\n\nexport function Modal({\n isOpen,\n onClose,\n title,\n children,\n size = 'md',\n}: {\n isOpen: boolean;\n onClose: () => void;\n title?: string;\n children: React.ReactNode;\n size?: keyof typeof sizeMap;\n}) {\n if (!isOpen) return null;\n\n const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (e.target === e.currentTarget) onClose();\n };\n\n return (\n <div\n onClick={handleOverlayClick}\n style={{\n position: 'fixed',\n inset: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 1000,\n }}\n >\n <div\n style={{\n backgroundColor: 'rgb(255, 255, 255)',\n borderRadius: '10px',\n boxShadow: '0 16px 24px -8px rgba(0, 0, 0, 0.3)',\n width: '100%',\n maxWidth: sizeMap[size],\n maxHeight: '85vh',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '4px 6px',\n borderBottom: `1px solid ${'rgb(199, 199, 204)'}`,\n }}\n >\n {title && (\n <h2\n style={{\n margin: 0,\n fontSize: '18px',\n fontWeight: '600',\n color: 'rgb(48, 51, 54)',\n }}\n >\n {title}\n </h2>\n )}\n <button\n onClick={onClose}\n aria-label=\"Close\"\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: '20px',\n color: 'rgb(89, 89, 94)',\n padding: '4px',\n lineHeight: '1',\n marginLeft: 'auto',\n }}\n >\n ×\n </button>\n </div>\n <div\n style={{\n padding: '6px',\n overflowY: 'auto',\n color: 'rgb(48, 51, 54)',\n fontSize: '16px',\n lineHeight: '1.5',\n }}\n >\n {children}\n </div>\n </div>\n </div>\n );\n}\n"
|
|
1440
1447
|
},
|
|
1441
1448
|
{
|
|
1442
1449
|
"id": "Notification",
|
|
@@ -1461,7 +1468,8 @@
|
|
|
1461
1468
|
"import { Notification } from '@imj_media/ui';\n\nexport default function Example() {\n return (\n <Notification\n open\n content={{\n title: 'Notification Title',\n subtitle: 'Subtitle',\n description:\n 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet.',\n }}\n leading={{ source: 'general' }}\n appearance={{ intent: 'preventiva', showCloseButton: true }}\n meta={{ presentation: 'popup', stackSummary: '5 notificaciones más' }}\n events={{}}\n />\n );\n}"
|
|
1462
1469
|
]
|
|
1463
1470
|
}
|
|
1464
|
-
]
|
|
1471
|
+
],
|
|
1472
|
+
"standaloneSnippet": "import React, { useState } from 'react';\n\nconst variantMap: Record<string, { color: string; icon: string }> = {\n info: { color: 'rgb(5, 181, 212)', icon: 'ℹ' },\n success: { color: 'rgb(33, 196, 94)', icon: '✓' },\n warning: { color: 'rgb(235, 179, 8)', icon: '⚠' },\n error: { color: 'rgb(240, 69, 69)', icon: '✕' },\n};\n\ninterface NotificationProps {\n title: string;\n message?: string;\n variant?: 'info' | 'success' | 'warning' | 'error';\n closable?: boolean;\n onClose?: () => void;\n}\n\nexport function Notification({\n title,\n message,\n variant = 'info',\n closable = false,\n onClose,\n}: NotificationProps) {\n const [visible, setVisible] = useState(true);\n const v = variantMap[variant];\n\n if (!visible) return null;\n\n const handleClose = () => {\n setVisible(false);\n onClose?.();\n };\n\n return (\n <div\n role=\"alert\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '4px',\n backgroundColor: 'rgb(255, 255, 255)',\n borderRadius: '8px',\n borderLeft: `4px solid ${v.color}`,\n boxShadow: '0 4px 8px -2px rgba(0, 0, 0, 0.2)',\n }}\n >\n <span\n style={{\n flexShrink: 0,\n width: 24,\n height: 24,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '16px',\n color: v.color,\n }}\n >\n {v.icon}\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div\n style={{\n fontWeight: '600',\n fontSize: '14px',\n color: 'rgb(48, 51, 54)',\n marginBottom: message ? '4px' : 0,\n }}\n >\n {title}\n </div>\n {message && (\n <div\n style={{\n fontSize: '14px',\n color: 'rgb(89, 89, 94)',\n }}\n >\n {message}\n </div>\n )}\n </div>\n {closable && (\n <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Close notification\"\n style={{\n flexShrink: 0,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'rgb(89, 89, 94)',\n fontSize: '16px',\n padding: '4px',\n lineHeight: 1,\n }}\n >\n \\u00d7\n </button>\n )}\n </div>\n );\n}\n"
|
|
1465
1473
|
},
|
|
1466
1474
|
{
|
|
1467
1475
|
"id": "Pagination",
|
|
@@ -1954,7 +1962,8 @@
|
|
|
1954
1962
|
"import { Spinner } from '@imj_media/ui';\n\n export default function Example() {\n return <Spinner />\n }"
|
|
1955
1963
|
]
|
|
1956
1964
|
}
|
|
1957
|
-
]
|
|
1965
|
+
],
|
|
1966
|
+
"standaloneSnippet": "import React from 'react';\n\nconst sizeMap: Record<string, number> = {\n sm: 16,\n md: 24,\n lg: 40,\n};\n\nconst colorMap: Record<string, string> = {\n primary: 'rgb(54, 89, 194)',\n secondary: 'rgb(240, 69, 69)',\n tertiary: 'rgb(33, 196, 94)',\n destructive: 'rgb(235, 179, 8)',\n};\n\ninterface SpinnerProps {\n size?: 'sm' | 'md' | 'lg';\n color?: 'primary' | 'secondary' | 'tertiary' | 'destructive';\n}\n\nexport function Spinner({ size = 'md', color = 'primary' }: SpinnerProps) {\n const px = sizeMap[size];\n const borderWidth = Math.max(2, Math.round(px / 8));\n const c = colorMap[color];\n\n return (\n <>\n <style>{`@keyframes ui-spin { to { transform: rotate(360deg) } }`}</style>\n <div\n role=\"status\"\n aria-label=\"Loading\"\n style={{\n width: px,\n height: px,\n border: `${borderWidth}px solid ${c}33`,\n borderTopColor: c,\n borderRadius: '50%',\n animation: 'ui-spin 0.6s linear infinite',\n display: 'inline-block',\n }}\n />\n </>\n );\n}\n"
|
|
1958
1967
|
},
|
|
1959
1968
|
{
|
|
1960
1969
|
"id": "Stepper",
|
|
@@ -2170,7 +2179,8 @@
|
|
|
2170
2179
|
}
|
|
2171
2180
|
}
|
|
2172
2181
|
}
|
|
2173
|
-
]
|
|
2182
|
+
],
|
|
2183
|
+
"standaloneSnippet": "import React from 'react';\n\ninterface TabItem {\n id: string;\n label: string;\n content: React.ReactNode;\n}\n\ninterface TabsProps {\n items: TabItem[];\n activeTab: string;\n onChange: (id: string) => void;\n}\n\nexport function Tabs({ items, activeTab, onChange }: TabsProps) {\n const activeItem = items.find((item) => item.id === activeTab);\n\n return (\n <div>\n <div\n style={{\n display: 'flex',\n borderBottom: '1px solid rgb(199, 199, 204)',\n gap: '4px',\n }}\n >\n {items.map((item) => {\n const isActive = item.id === activeTab;\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => onChange(item.id)}\n style={{\n padding: '2px 4px',\n border: 'none',\n borderBottom: isActive\n ? '2px solid rgb(54, 89, 194)'\n : '2px solid transparent',\n background: 'none',\n cursor: 'pointer',\n fontSize: '14px',\n fontWeight: isActive\n ? '600'\n : '400',\n color: isActive\n ? 'rgb(54, 89, 194)'\n : 'rgb(89, 89, 94)',\n transition: 'color 0.15s, border-color 0.15s',\n }}\n onMouseEnter={(e) => {\n if (!isActive) e.currentTarget.style.color = 'rgb(48, 51, 54)';\n }}\n onMouseLeave={(e) => {\n if (!isActive) e.currentTarget.style.color = 'rgb(89, 89, 94)';\n }}\n >\n {item.label}\n </button>\n );\n })}\n </div>\n <div style={{ padding: '4px 0' }}>\n {activeItem ? activeItem.content : null}\n </div>\n </div>\n );\n}\n"
|
|
2174
2184
|
},
|
|
2175
2185
|
{
|
|
2176
2186
|
"id": "Tag",
|
|
@@ -2195,7 +2205,8 @@
|
|
|
2195
2205
|
"import {\n faCircleInfo,\n faFilter,\n faSearch,\n } from '@fortawesome/pro-regular-svg-icons';\n\n <Tag\n label='\"query\"'\n color=\"success\"\n infoIcon={{ name: faSearch }}\n onClose={() => {}}\n />\n <Tag\n label=\"Custom\"\n color=\"info\"\n infoIcon={{ name: faCircleInfo, size: 'sm', color: 'warning' }}\n />"
|
|
2196
2206
|
]
|
|
2197
2207
|
}
|
|
2198
|
-
]
|
|
2208
|
+
],
|
|
2209
|
+
"standaloneSnippet": "import React from 'react';\n\nconst colorMap: Record<string, { bg: string; text: string }> = {\n primary: { bg: 'rgb(54, 89, 194)1a', text: 'rgb(54, 89, 194)' },\n secondary: { bg: 'rgb(240, 69, 69)1a', text: 'rgb(240, 69, 69)' },\n tertiary: { bg: 'rgb(33, 196, 94)1a', text: 'rgb(33, 196, 94)' },\n destructive: { bg: 'rgb(235, 179, 8)1a', text: 'rgb(235, 179, 8)' },\n neutral: { bg: 'rgb(247, 247, 250)', text: 'rgb(89, 89, 94)' },\n};\n\nconst sizeMap: Record<string, { fontSize: string; padding: string }> = {\n sm: { fontSize: '12px', padding: '2px 6px' },\n md: { fontSize: '14px', padding: '4px 2px' },\n};\n\ninterface TagProps {\n children: React.ReactNode;\n color?: 'primary' | 'secondary' | 'tertiary' | 'destructive' | 'neutral';\n size?: 'sm' | 'md';\n closable?: boolean;\n onClose?: () => void;\n}\n\nexport function Tag({\n children,\n color = 'primary',\n size = 'md',\n closable = false,\n onClose,\n}: TagProps) {\n const c = colorMap[color];\n const s = sizeMap[size];\n\n return (\n <span\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n backgroundColor: c.bg,\n color: c.text,\n borderRadius: '8px',\n padding: s.padding,\n fontSize: s.fontSize,\n fontWeight: '500',\n lineHeight: 1.4,\n }}\n >\n {children}\n {closable && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Remove\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: 'inherit',\n padding: 0,\n fontSize: 'inherit',\n lineHeight: 1,\n opacity: 0.7,\n }}\n >\n \\u00d7\n </button>\n )}\n </span>\n );\n}\n"
|
|
2199
2210
|
},
|
|
2200
2211
|
{
|
|
2201
2212
|
"id": "Text",
|
|
@@ -2626,7 +2637,8 @@
|
|
|
2626
2637
|
},
|
|
2627
2638
|
"examples": []
|
|
2628
2639
|
}
|
|
2629
|
-
]
|
|
2640
|
+
],
|
|
2641
|
+
"standaloneSnippet": "import React from 'react';\n\nconst sizeDims: Record<string, { track: [number, number]; knob: number; offset: number }> = {\n sm: { track: [28, 16], knob: 12, offset: 2 },\n md: { track: [36, 20], knob: 16, offset: 2 },\n lg: { track: [44, 24], knob: 20, offset: 2 },\n};\n\ninterface ToggleProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n size?: 'sm' | 'md' | 'lg';\n}\n\nexport function Toggle({\n checked,\n onChange,\n disabled = false,\n size = 'md',\n}: ToggleProps) {\n const d = sizeDims[size];\n const [w, h] = d.track;\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n onClick={() => onChange(!checked)}\n style={{\n position: 'relative',\n display: 'inline-flex',\n alignItems: 'center',\n width: w,\n height: h,\n borderRadius: h,\n border: 'none',\n padding: 0,\n cursor: disabled ? 'not-allowed' : 'pointer',\n backgroundColor: checked ? 'rgb(54, 89, 194)' : 'rgb(247, 247, 250)',\n opacity: disabled ? 0.5 : 1,\n transition: 'background-color 0.2s',\n }}\n >\n <span\n style={{\n position: 'absolute',\n top: d.offset,\n left: checked ? w - d.knob - d.offset : d.offset,\n width: d.knob,\n height: d.knob,\n borderRadius: '50%',\n backgroundColor: 'rgb(255, 255, 255)',\n transition: 'left 0.2s',\n boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.1)',\n }}\n />\n </button>\n );\n}\n"
|
|
2630
2642
|
},
|
|
2631
2643
|
{
|
|
2632
2644
|
"id": "Toolbar",
|
|
@@ -2704,7 +2716,8 @@
|
|
|
2704
2716
|
"<Tooltip label=\"Tooltip arriba\" position=\"top\">\n <InnerButton color=\"primary\">Top</InnerButton>\n </Tooltip>\n <Tooltip label=\"Tooltip abajo\" position=\"bottom\">\n <InnerButton color=\"secondary\">Bottom</InnerButton>\n </Tooltip>\n <Tooltip label=\"Tooltip izquierda\" position=\"left\">\n <InnerButton color=\"destructive\">Left</InnerButton>\n </Tooltip>\n <Tooltip label=\"Tooltip derecha\" position=\"right\">\n <InnerButton color=\"tertiary\">Right</InnerButton>\n </Tooltip>"
|
|
2705
2717
|
]
|
|
2706
2718
|
}
|
|
2707
|
-
]
|
|
2719
|
+
],
|
|
2720
|
+
"standaloneSnippet": "import React, { useState } from 'react';\n\nconst positionStyles: Record<string, React.CSSProperties> = {\n top: {\n bottom: '100%',\n left: '50%',\n transform: 'translateX(-50%)',\n marginBottom: '4px',\n },\n bottom: {\n top: '100%',\n left: '50%',\n transform: 'translateX(-50%)',\n marginTop: '4px',\n },\n left: {\n right: '100%',\n top: '50%',\n transform: 'translateY(-50%)',\n marginRight: '4px',\n },\n right: {\n left: '100%',\n top: '50%',\n transform: 'translateY(-50%)',\n marginLeft: '4px',\n },\n};\n\ninterface TooltipProps {\n children: React.ReactNode;\n content: React.ReactNode;\n position?: 'top' | 'bottom' | 'left' | 'right';\n}\n\nexport function Tooltip({ children, content, position = 'top' }: TooltipProps) {\n const [show, setShow] = useState(false);\n\n return (\n <div\n style={{ position: 'relative', display: 'inline-block' }}\n onMouseEnter={() => setShow(true)}\n onMouseLeave={() => setShow(false)}\n >\n {children}\n {show && (\n <span\n style={{\n position: 'absolute',\n whiteSpace: 'nowrap',\n backgroundColor: 'rgb(48, 51, 54)',\n color: 'rgb(255, 255, 255)',\n borderRadius: '8px',\n padding: '4px 2px',\n fontSize: '12px',\n pointerEvents: 'none',\n zIndex: 9999,\n ...positionStyles[position],\n }}\n >\n {content}\n </span>\n )}\n </div>\n );\n}\n"
|
|
2708
2721
|
},
|
|
2709
2722
|
{
|
|
2710
2723
|
"id": "Visual",
|
|
@@ -2958,5 +2971,90 @@
|
|
|
2958
2971
|
"title": "Imports del paquete",
|
|
2959
2972
|
"body": "Importa desde @imj_media/ui; usa import type para tipos. Revisa deprecations en get_deprecations."
|
|
2960
2973
|
}
|
|
2961
|
-
]
|
|
2974
|
+
],
|
|
2975
|
+
"tokenPalette": {
|
|
2976
|
+
"colors": {
|
|
2977
|
+
"brand": {
|
|
2978
|
+
"default": "rgb(54, 89, 194)",
|
|
2979
|
+
"hover": "rgb(51, 82, 176)",
|
|
2980
|
+
"pressed": "rgb(46, 74, 161)"
|
|
2981
|
+
},
|
|
2982
|
+
"secondary": {
|
|
2983
|
+
"default": "rgb(240, 69, 69)",
|
|
2984
|
+
"hover": "rgb(212, 61, 61)",
|
|
2985
|
+
"pressed": "rgb(184, 51, 51)"
|
|
2986
|
+
},
|
|
2987
|
+
"tertiary": {
|
|
2988
|
+
"default": "rgb(33, 196, 94)",
|
|
2989
|
+
"hover": "rgb(31, 181, 87)",
|
|
2990
|
+
"pressed": "rgb(28, 163, 79)"
|
|
2991
|
+
},
|
|
2992
|
+
"destructive": {
|
|
2993
|
+
"default": "rgb(235, 179, 8)",
|
|
2994
|
+
"hover": "rgb(209, 158, 8)",
|
|
2995
|
+
"pressed": "rgb(181, 140, 5)"
|
|
2996
|
+
},
|
|
2997
|
+
"text": {
|
|
2998
|
+
"primary": "rgb(48, 51, 54)",
|
|
2999
|
+
"secondary": "rgb(89, 89, 94)",
|
|
3000
|
+
"disabled": "rgb(186, 189, 191)",
|
|
3001
|
+
"onFill": "rgb(255, 255, 255)"
|
|
3002
|
+
},
|
|
3003
|
+
"bg": {
|
|
3004
|
+
"canvas": "rgb(247, 247, 250)",
|
|
3005
|
+
"surface": "rgb(255, 255, 255)",
|
|
3006
|
+
"muted": "rgb(247, 247, 250)"
|
|
3007
|
+
},
|
|
3008
|
+
"border": {
|
|
3009
|
+
"default": "rgb(199, 199, 204)",
|
|
3010
|
+
"strong": "rgb(77, 79, 84)"
|
|
3011
|
+
},
|
|
3012
|
+
"status": {
|
|
3013
|
+
"success": "rgb(33, 196, 94)",
|
|
3014
|
+
"warning": "rgb(235, 179, 8)",
|
|
3015
|
+
"error": "rgb(240, 69, 69)",
|
|
3016
|
+
"info": "rgb(5, 181, 212)"
|
|
3017
|
+
}
|
|
3018
|
+
},
|
|
3019
|
+
"spacing": {
|
|
3020
|
+
"0": "0",
|
|
3021
|
+
"2": "2px",
|
|
3022
|
+
"4": "4px",
|
|
3023
|
+
"6": "6px",
|
|
3024
|
+
"8": "8px",
|
|
3025
|
+
"12": "12px",
|
|
3026
|
+
"16": "16px",
|
|
3027
|
+
"20": "20px",
|
|
3028
|
+
"24": "24px",
|
|
3029
|
+
"32": "32px",
|
|
3030
|
+
"40": "40px",
|
|
3031
|
+
"48": "48px",
|
|
3032
|
+
"64": "64px",
|
|
3033
|
+
"96": "96px"
|
|
3034
|
+
},
|
|
3035
|
+
"radius": {
|
|
3036
|
+
"sm": "8px",
|
|
3037
|
+
"md": "8px",
|
|
3038
|
+
"lg": "10px",
|
|
3039
|
+
"full": "9999px"
|
|
3040
|
+
},
|
|
3041
|
+
"fontSize": {
|
|
3042
|
+
"xs": "12px",
|
|
3043
|
+
"sm": "14px",
|
|
3044
|
+
"md": "16px",
|
|
3045
|
+
"lg": "18px",
|
|
3046
|
+
"xl": "20px"
|
|
3047
|
+
},
|
|
3048
|
+
"fontWeight": {
|
|
3049
|
+
"normal": "400",
|
|
3050
|
+
"medium": "500",
|
|
3051
|
+
"semibold": "600",
|
|
3052
|
+
"bold": "700"
|
|
3053
|
+
},
|
|
3054
|
+
"shadow": {
|
|
3055
|
+
"sm": "0 1px 2px 0 rgba(0, 0, 0, 0.1)",
|
|
3056
|
+
"md": "0 4px 8px -2px rgba(0, 0, 0, 0.2)",
|
|
3057
|
+
"lg": "0 16px 24px -8px rgba(0, 0, 0, 0.3)"
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
2962
3060
|
}
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"consumerChecklist": {
|
|
31
31
|
"type": "array",
|
|
32
32
|
"items": { "$ref": "#/$defs/consumerChecklistItem" }
|
|
33
|
-
}
|
|
33
|
+
},
|
|
34
|
+
"tokenPalette": { "$ref": "#/$defs/tokenPalette" }
|
|
34
35
|
},
|
|
35
36
|
"$defs": {
|
|
36
37
|
"mergedDescription": {
|
|
@@ -66,7 +67,8 @@
|
|
|
66
67
|
},
|
|
67
68
|
"description": { "$ref": "#/$defs/mergedDescription" },
|
|
68
69
|
"examples": { "type": "array", "items": { "type": "string" } },
|
|
69
|
-
"props": { "$ref": "#/$defs/exportProps" }
|
|
70
|
+
"props": { "$ref": "#/$defs/exportProps" },
|
|
71
|
+
"demoProps": { "$ref": "#/$defs/demoPropsEntry" }
|
|
70
72
|
}
|
|
71
73
|
},
|
|
72
74
|
"exportProps": {
|
|
@@ -97,7 +99,42 @@
|
|
|
97
99
|
"legacyReason": { "type": "string" },
|
|
98
100
|
"compositionType": { "enum": [1, 2] },
|
|
99
101
|
"exports": { "type": "array", "items": { "$ref": "#/$defs/moduleExport" } },
|
|
100
|
-
"compositionRecipe": { "$ref": "#/$defs/compositionRecipe" }
|
|
102
|
+
"compositionRecipe": { "$ref": "#/$defs/compositionRecipe" },
|
|
103
|
+
"standaloneSnippet": { "type": "string" },
|
|
104
|
+
"demoProps": { "$ref": "#/$defs/demoPropsEntry" },
|
|
105
|
+
"previewUnavailable": {
|
|
106
|
+
"enum": ["composite", "no_template", "deprecated", "internal_only"]
|
|
107
|
+
},
|
|
108
|
+
"previewScreenshotUrl": { "type": "string" }
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"demoPropsEntry": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"required": ["props"],
|
|
114
|
+
"properties": {
|
|
115
|
+
"props": { "type": "object" },
|
|
116
|
+
"variants": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": ["name", "props"],
|
|
121
|
+
"properties": {
|
|
122
|
+
"name": { "type": "string" },
|
|
123
|
+
"props": { "type": "object" }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"tokenPalette": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"properties": {
|
|
132
|
+
"colors": { "type": "object" },
|
|
133
|
+
"spacing": { "type": "object" },
|
|
134
|
+
"radius": { "type": "object" },
|
|
135
|
+
"fontSize": { "type": "object" },
|
|
136
|
+
"fontWeight": { "type": "object" },
|
|
137
|
+
"shadow": { "type": "object" }
|
|
101
138
|
}
|
|
102
139
|
},
|
|
103
140
|
"styleIndex": {
|