@hanzo/ui 8.0.82 → 8.0.86
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/backends/gui/button.d.ts +3 -3
- package/dist/backends/gui/card.d.ts.map +1 -1
- package/dist/backends/gui/card.js.map +1 -1
- package/dist/backends/gui/command.js.map +1 -1
- package/dist/backends/gui/context-menu.d.ts +3 -3
- package/dist/backends/gui/dropdown-menu.d.ts +4 -4
- package/dist/backends/gui/resizable.cjs +4 -1
- package/dist/backends/gui/resizable.d.ts.map +1 -1
- package/dist/backends/gui/resizable.js +4 -1
- package/dist/backends/gui/resizable.js.map +1 -1
- package/dist/backends/gui/scroll-area.d.ts.map +1 -1
- package/dist/backends/gui/scroll-area.js.map +1 -1
- package/dist/backends/gui/textarea.js.map +1 -1
- package/dist/box.cjs +31 -0
- package/dist/box.d.ts +26 -0
- package/dist/box.d.ts.map +1 -0
- package/dist/box.js +29 -0
- package/dist/box.js.map +1 -0
- package/dist/chat/Thread.d.ts.map +1 -1
- package/dist/chat/Thread.js.map +1 -1
- package/dist/gallery.cjs +2 -1
- package/dist/gallery.d.ts.map +1 -1
- package/dist/gallery.js +2 -1
- package/dist/gallery.js.map +1 -1
- package/dist/gui-config.cjs +8 -2
- package/dist/gui-config.d.ts +469 -461
- package/dist/gui-config.d.ts.map +1 -1
- package/dist/gui-config.js +8 -2
- package/dist/gui-config.js.map +1 -1
- package/dist/index.cjs +5 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/models/ModelSelector.js.map +1 -1
- package/dist/styles.css +10 -0
- package/dist/tw.cjs +468 -0
- package/dist/tw.d.ts +31 -0
- package/dist/tw.d.ts.map +1 -0
- package/dist/tw.js +466 -0
- package/dist/tw.js.map +1 -0
- package/package.json +10 -2
package/dist/tw.cjs
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A utility class and a gui style prop are two notations for one value: `px-4`
|
|
4
|
+
* and `px={16}` say the same thing. This is the function between them.
|
|
5
|
+
*
|
|
6
|
+
* It exists so an app carrying a hundred thousand utility classes can render
|
|
7
|
+
* through gui without either rewriting every class first or keeping a second
|
|
8
|
+
* styling engine around to read them. Convert the notation, delete the engine.
|
|
9
|
+
*
|
|
10
|
+
* Values are CONCRETE — `p-4` is 16, not `$4`. The token scale is where these
|
|
11
|
+
* apps are going, but a token substitution moves every measurement on the page
|
|
12
|
+
* at the same time as the engine underneath it changes, and then nothing
|
|
13
|
+
* identifies which of the two moved it. Retheming is its own step.
|
|
14
|
+
*
|
|
15
|
+
* A class this does not know is returned in `rest`, never dropped. A caller
|
|
16
|
+
* passes it through as a class name, so an unconverted page degrades to what it
|
|
17
|
+
* does today rather than to an unstyled one.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.tw = tw;
|
|
21
|
+
/** Tailwind's 4px ramp, and the three fractional stops below it. */
|
|
22
|
+
const SPACE = {
|
|
23
|
+
'0': 0, px: 1, '0.5': 2, '1': 4, '1.5': 6, '2': 8, '2.5': 10, '3': 12,
|
|
24
|
+
'3.5': 14, '4': 16, '5': 20, '6': 24, '7': 28, '8': 32, '9': 36, '10': 40,
|
|
25
|
+
'11': 44, '12': 48, '14': 56, '16': 64, '20': 80, '24': 96, '28': 112,
|
|
26
|
+
'32': 128, '36': 144, '40': 160, '44': 176, '48': 192, '52': 208, '56': 224,
|
|
27
|
+
'60': 240, '64': 256, '72': 288, '80': 320, '96': 384,
|
|
28
|
+
};
|
|
29
|
+
const FONT_SIZE = {
|
|
30
|
+
xs: 12, sm: 14, base: 16, lg: 18, xl: 20, '2xl': 24, '3xl': 30, '4xl': 36,
|
|
31
|
+
'5xl': 48, '6xl': 60, '7xl': 72, '8xl': 96, '9xl': 128,
|
|
32
|
+
};
|
|
33
|
+
const WEIGHT = {
|
|
34
|
+
thin: 100, extralight: 200, light: 300, normal: 400, medium: 500,
|
|
35
|
+
semibold: 600, bold: 700, extrabold: 800, black: 900,
|
|
36
|
+
};
|
|
37
|
+
const RADIUS = {
|
|
38
|
+
none: 0, sm: 2, '': 4, md: 6, lg: 8, xl: 12, '2xl': 16, '3xl': 24, full: 9999,
|
|
39
|
+
};
|
|
40
|
+
const LEADING = {
|
|
41
|
+
none: 1, tight: 1.25, snug: 1.375, normal: 1.5, relaxed: 1.625, loose: 2,
|
|
42
|
+
};
|
|
43
|
+
const TRACKING = {
|
|
44
|
+
tighter: -0.8, tight: -0.4, normal: 0, wide: 0.4, wider: 0.8, widest: 1.6,
|
|
45
|
+
};
|
|
46
|
+
/** Flex words Tailwind and the style property spell differently. */
|
|
47
|
+
const ALIGN = {
|
|
48
|
+
start: 'flex-start', end: 'flex-end', center: 'center',
|
|
49
|
+
between: 'space-between', around: 'space-around', evenly: 'space-evenly',
|
|
50
|
+
stretch: 'stretch', baseline: 'baseline',
|
|
51
|
+
};
|
|
52
|
+
const SIDE = { t: 'Top', r: 'Right', b: 'Bottom', l: 'Left' };
|
|
53
|
+
/** Longest first, so `max-w` wins over `max` and `gap-x` over `gap`. */
|
|
54
|
+
const HEADS = [
|
|
55
|
+
'min-w', 'min-h', 'max-w', 'max-h', 'grid-cols', 'grid-rows', 'col-span',
|
|
56
|
+
'row-span', 'translate-x', 'translate-y', 'gap-x', 'gap-y', 'space-x', 'space-y', 'overflow-x', 'overflow-y',
|
|
57
|
+
'transition', 'duration', 'delay', 'ease', 'blur', 'backdrop-blur', 'shadow',
|
|
58
|
+
'overflow',
|
|
59
|
+
'tracking', 'leading', 'rounded', 'border', 'divide', 'aspect', 'object',
|
|
60
|
+
'whitespace', 'align', 'inset', 'items', 'justify', 'content', 'self',
|
|
61
|
+
'opacity', 'text', 'font', 'bg', 'gap', 'top', 'right', 'bottom', 'left',
|
|
62
|
+
'w', 'h', 'z', 'p', 'px', 'py', 'pt', 'pr', 'pb', 'pl',
|
|
63
|
+
'm', 'mx', 'my', 'mt', 'mr', 'mb', 'ml',
|
|
64
|
+
].sort((a, b) => b.length - a.length);
|
|
65
|
+
/** Tailwind names the gradient's destination; CSS names its angle. */
|
|
66
|
+
const DIRECTION = {
|
|
67
|
+
t: 'to top', tr: 'to top right', r: 'to right', br: 'to bottom right',
|
|
68
|
+
b: 'to bottom', bl: 'to bottom left', l: 'to left', tl: 'to top left',
|
|
69
|
+
};
|
|
70
|
+
const FAMILY = {
|
|
71
|
+
sans: 'var(--font-sans)', serif: 'var(--font-serif)', mono: 'var(--font-mono)',
|
|
72
|
+
};
|
|
73
|
+
const DURATION = {
|
|
74
|
+
'0': 0, '75': 75, '100': 100, '150': 150, '200': 200, '300': 300,
|
|
75
|
+
'500': 500, '700': 700, '1000': 1000,
|
|
76
|
+
};
|
|
77
|
+
/** What Tailwind animates under each word. */
|
|
78
|
+
const TRANSITION = {
|
|
79
|
+
none: 'none', all: 'all', DEFAULT: 'color, background-color, border-color, opacity, box-shadow, transform',
|
|
80
|
+
colors: 'color, background-color, border-color, text-decoration-color, fill, stroke',
|
|
81
|
+
opacity: 'opacity',
|
|
82
|
+
shadow: 'box-shadow', transform: 'transform',
|
|
83
|
+
};
|
|
84
|
+
const SHADOW = {
|
|
85
|
+
none: 'none', sm: '0 1px 2px rgb(0 0 0 / .05)',
|
|
86
|
+
DEFAULT: '0 1px 3px rgb(0 0 0 / .1), 0 1px 2px rgb(0 0 0 / .06)',
|
|
87
|
+
md: '0 4px 6px rgb(0 0 0 / .1), 0 2px 4px rgb(0 0 0 / .06)',
|
|
88
|
+
lg: '0 10px 15px rgb(0 0 0 / .1), 0 4px 6px rgb(0 0 0 / .05)',
|
|
89
|
+
xl: '0 20px 25px rgb(0 0 0 / .1), 0 10px 10px rgb(0 0 0 / .04)',
|
|
90
|
+
'2xl': '0 25px 50px rgb(0 0 0 / .25)', inner: 'inset 0 2px 4px rgb(0 0 0 / .06)',
|
|
91
|
+
};
|
|
92
|
+
const BLUR = {
|
|
93
|
+
none: 0, xs: 4, sm: 8, DEFAULT: 8, md: 12, lg: 16, xl: 24, '2xl': 40, '3xl': 64,
|
|
94
|
+
};
|
|
95
|
+
/** A size word, a fraction, or a step on the ramp. */
|
|
96
|
+
function size(v) {
|
|
97
|
+
if (v === 'full')
|
|
98
|
+
return '100%';
|
|
99
|
+
if (v === 'screen')
|
|
100
|
+
return '100vh';
|
|
101
|
+
if (v === 'auto' || v === 'fit' || v === 'min' || v === 'max')
|
|
102
|
+
return v === 'fit' ? 'fit-content' : v;
|
|
103
|
+
if (v in SPACE)
|
|
104
|
+
return SPACE[v];
|
|
105
|
+
const frac = /^(\d+)\/(\d+)$/.exec(v);
|
|
106
|
+
if (frac)
|
|
107
|
+
return `${((+frac[1] / +frac[2]) * 100).toFixed(4).replace(/\.?0+$/, '')}%`;
|
|
108
|
+
const arb = /^\[(.+)]$/.exec(v);
|
|
109
|
+
if (arb)
|
|
110
|
+
return arb[1];
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* A colour is a CSS custom property, so `text-foreground` stays exactly the
|
|
115
|
+
* value the sheet already resolves — the palette is not restated here. The
|
|
116
|
+
* `/NN` suffix is an alpha, which `color-mix` applies without needing to know
|
|
117
|
+
* what the variable holds.
|
|
118
|
+
*/
|
|
119
|
+
function color(v) {
|
|
120
|
+
if (v === 'transparent')
|
|
121
|
+
return 'transparent';
|
|
122
|
+
if (v === 'current')
|
|
123
|
+
return 'currentColor';
|
|
124
|
+
if (v === 'white')
|
|
125
|
+
return '#fff';
|
|
126
|
+
if (v === 'black')
|
|
127
|
+
return '#000';
|
|
128
|
+
const m = /^(.+?)(?:\/(\d{1,3}))?$/.exec(v);
|
|
129
|
+
if (!m)
|
|
130
|
+
return undefined;
|
|
131
|
+
const [, name, alpha] = m;
|
|
132
|
+
const arb = /^\[(.+)]$/.exec(name);
|
|
133
|
+
const base = arb ? arb[1]
|
|
134
|
+
: name === 'white' ? '#fff' : name === 'black' ? '#000'
|
|
135
|
+
: `var(--${name})`;
|
|
136
|
+
if (!alpha)
|
|
137
|
+
return base;
|
|
138
|
+
return `color-mix(in srgb, ${base} ${alpha}%, transparent)`;
|
|
139
|
+
}
|
|
140
|
+
/** Properties a negative class may set — the rest have no negative form. */
|
|
141
|
+
const NEGATABLE = /^(margin|top|right|bottom|left|zIndex|translate)/;
|
|
142
|
+
/** One class to zero or more props. Returns null when the class is unknown. */
|
|
143
|
+
function one(c) {
|
|
144
|
+
// display and the flex words
|
|
145
|
+
switch (c) {
|
|
146
|
+
case 'flex': return { display: 'flex', flexDirection: 'row' };
|
|
147
|
+
case 'inline-flex': return { display: 'inline-flex', flexDirection: 'row' };
|
|
148
|
+
case 'grid': return { display: 'grid' };
|
|
149
|
+
case 'block': return { display: 'block' };
|
|
150
|
+
case 'inline': return { display: 'inline' };
|
|
151
|
+
case 'inline-block': return { display: 'inline-block' };
|
|
152
|
+
case 'hidden': return { display: 'none' };
|
|
153
|
+
case 'flex-col': return { flexDirection: 'column' };
|
|
154
|
+
case 'flex-row': return { flexDirection: 'row' };
|
|
155
|
+
case 'flex-col-reverse': return { flexDirection: 'column-reverse' };
|
|
156
|
+
case 'flex-row-reverse': return { flexDirection: 'row-reverse' };
|
|
157
|
+
case 'flex-wrap': return { flexWrap: 'wrap' };
|
|
158
|
+
case 'flex-nowrap': return { flexWrap: 'nowrap' };
|
|
159
|
+
case 'flex-1': return { flex: 1 };
|
|
160
|
+
case 'flex-auto': return { flex: 'auto' };
|
|
161
|
+
case 'flex-none': return { flex: 'none' };
|
|
162
|
+
case 'flex-initial': return { flex: 'initial' };
|
|
163
|
+
case 'flex-shrink-0':
|
|
164
|
+
case 'shrink-0': return { flexShrink: 0 };
|
|
165
|
+
case 'flex-grow':
|
|
166
|
+
case 'grow': return { flexGrow: 1 };
|
|
167
|
+
case 'flex-grow-0':
|
|
168
|
+
case 'grow-0': return { flexGrow: 0 };
|
|
169
|
+
case 'absolute': return { position: 'absolute' };
|
|
170
|
+
case 'relative': return { position: 'relative' };
|
|
171
|
+
case 'fixed': return { position: 'fixed' };
|
|
172
|
+
case 'sticky': return { position: 'sticky' };
|
|
173
|
+
case 'static': return { position: 'static' };
|
|
174
|
+
case 'inset-0': return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
175
|
+
case 'uppercase': return { textTransform: 'uppercase' };
|
|
176
|
+
case 'lowercase': return { textTransform: 'lowercase' };
|
|
177
|
+
case 'capitalize': return { textTransform: 'capitalize' };
|
|
178
|
+
case 'normal-case': return { textTransform: 'none' };
|
|
179
|
+
case 'italic': return { fontStyle: 'italic' };
|
|
180
|
+
case 'not-italic': return { fontStyle: 'normal' };
|
|
181
|
+
case 'underline': return { textDecorationLine: 'underline' };
|
|
182
|
+
case 'line-through': return { textDecorationLine: 'line-through' };
|
|
183
|
+
case 'no-underline': return { textDecorationLine: 'none' };
|
|
184
|
+
case 'truncate': return { overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' };
|
|
185
|
+
case 'mx-auto': return { marginLeft: 'auto', marginRight: 'auto' };
|
|
186
|
+
case 'my-auto': return { marginTop: 'auto', marginBottom: 'auto' };
|
|
187
|
+
case 'border': return { borderWidth: 1 };
|
|
188
|
+
case 'rounded': return { borderRadius: RADIUS[''] };
|
|
189
|
+
case 'cursor-pointer': return { cursor: 'pointer' };
|
|
190
|
+
case 'select-none': return { userSelect: 'none' };
|
|
191
|
+
case 'pointer-events-none': return { pointerEvents: 'none' };
|
|
192
|
+
case 'sr-only': return { position: 'absolute', width: 1, height: 1, overflow: 'hidden' };
|
|
193
|
+
case 'tabular-nums': return { fontVariantNumeric: 'tabular-nums' };
|
|
194
|
+
case 'transform': return { transform: 'translateZ(0)' };
|
|
195
|
+
case 'transition': return { transitionProperty: TRANSITION.DEFAULT };
|
|
196
|
+
case 'blur': return { filter: `blur(${BLUR.DEFAULT}px)` };
|
|
197
|
+
case 'shadow': return { boxShadow: SHADOW.DEFAULT };
|
|
198
|
+
}
|
|
199
|
+
// A leading `-` negates whatever the rest of the class resolves to.
|
|
200
|
+
if (c.startsWith('-')) {
|
|
201
|
+
const pos = one(c.slice(1));
|
|
202
|
+
if (!pos)
|
|
203
|
+
return null;
|
|
204
|
+
const out = {};
|
|
205
|
+
for (const k in pos) {
|
|
206
|
+
const v = pos[k];
|
|
207
|
+
out[k] = typeof v === 'number' && NEGATABLE.test(k) ? -v
|
|
208
|
+
: typeof v === 'string' && /^[\d.]+%$/.test(v) && NEGATABLE.test(k) ? `-${v}`
|
|
209
|
+
: v;
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
// Heads are matched longest-first. A shortest-match split reads `max-w-3xl`
|
|
214
|
+
// as `max` + `w-3xl`, so every compound head silently fell through.
|
|
215
|
+
let head = '', v = '';
|
|
216
|
+
for (const h of HEADS) {
|
|
217
|
+
if (c.length > h.length + 1 && c.startsWith(h + '-')) {
|
|
218
|
+
head = h;
|
|
219
|
+
v = c.slice(h.length + 1);
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!head)
|
|
224
|
+
return null;
|
|
225
|
+
// padding and margin, whole box or one side
|
|
226
|
+
const box = /^([pm])([xytrbl])?$/.exec(head);
|
|
227
|
+
if (box) {
|
|
228
|
+
const [, kind, where] = box;
|
|
229
|
+
const n = v === 'auto' ? 'auto' : SPACE[v] ?? size(v);
|
|
230
|
+
if (n === undefined)
|
|
231
|
+
return null;
|
|
232
|
+
const base = kind === 'p' ? 'padding' : 'margin';
|
|
233
|
+
if (!where)
|
|
234
|
+
return { [base]: n };
|
|
235
|
+
if (where === 'x')
|
|
236
|
+
return { [`${base}Left`]: n, [`${base}Right`]: n };
|
|
237
|
+
if (where === 'y')
|
|
238
|
+
return { [`${base}Top`]: n, [`${base}Bottom`]: n };
|
|
239
|
+
return { [`${base}${SIDE[where]}`]: n };
|
|
240
|
+
}
|
|
241
|
+
switch (head) {
|
|
242
|
+
case 'gap': {
|
|
243
|
+
const n = SPACE[v] ?? size(v);
|
|
244
|
+
return n === undefined ? null : { gap: n };
|
|
245
|
+
}
|
|
246
|
+
case 'gap-x': {
|
|
247
|
+
const n = SPACE[v] ?? size(v);
|
|
248
|
+
return n === undefined ? null : { columnGap: n };
|
|
249
|
+
}
|
|
250
|
+
case 'gap-y': {
|
|
251
|
+
const n = SPACE[v] ?? size(v);
|
|
252
|
+
return n === undefined ? null : { rowGap: n };
|
|
253
|
+
}
|
|
254
|
+
case 'items': return ALIGN[v] ? { alignItems: ALIGN[v] } : null;
|
|
255
|
+
case 'justify': return ALIGN[v] ? { justifyContent: ALIGN[v] } : null;
|
|
256
|
+
case 'self': return ALIGN[v] ? { alignSelf: ALIGN[v] } : null;
|
|
257
|
+
case 'content': return ALIGN[v] ? { alignContent: ALIGN[v] } : null;
|
|
258
|
+
case 'w': {
|
|
259
|
+
const n = size(v);
|
|
260
|
+
return n === undefined ? null : { width: n };
|
|
261
|
+
}
|
|
262
|
+
case 'h': {
|
|
263
|
+
const n = v === 'screen' ? '100vh' : size(v);
|
|
264
|
+
return n === undefined ? null : { height: n };
|
|
265
|
+
}
|
|
266
|
+
case 'min-w': {
|
|
267
|
+
const n = size(v);
|
|
268
|
+
return n === undefined ? null : { minWidth: n };
|
|
269
|
+
}
|
|
270
|
+
case 'min-h': {
|
|
271
|
+
const n = v === 'screen' ? '100vh' : size(v);
|
|
272
|
+
return n === undefined ? null : { minHeight: n };
|
|
273
|
+
}
|
|
274
|
+
case 'max-w': {
|
|
275
|
+
const n = MAX_W[v] ?? size(v);
|
|
276
|
+
return n === undefined ? null : { maxWidth: n };
|
|
277
|
+
}
|
|
278
|
+
case 'max-h': {
|
|
279
|
+
const n = v === 'screen' ? '100vh' : size(v);
|
|
280
|
+
return n === undefined ? null : { maxHeight: n };
|
|
281
|
+
}
|
|
282
|
+
case 'text': {
|
|
283
|
+
if (v in FONT_SIZE)
|
|
284
|
+
return { fontSize: FONT_SIZE[v] };
|
|
285
|
+
if (v === 'left' || v === 'center' || v === 'right' || v === 'justify')
|
|
286
|
+
return { textAlign: v };
|
|
287
|
+
const col = color(v);
|
|
288
|
+
return col ? { color: col } : null;
|
|
289
|
+
}
|
|
290
|
+
case 'bg': {
|
|
291
|
+
const col = color(v);
|
|
292
|
+
return col ? { backgroundColor: col } : null;
|
|
293
|
+
}
|
|
294
|
+
case 'font': return WEIGHT[v] !== undefined ? { fontWeight: WEIGHT[v] }
|
|
295
|
+
: FAMILY[v] ? { fontFamily: FAMILY[v] } : null;
|
|
296
|
+
case 'leading': {
|
|
297
|
+
const arb = /^\[(.+)]$/.exec(v);
|
|
298
|
+
const n = arb ? Number(arb[1]) || arb[1] : LEADING[v] ?? SPACE[v];
|
|
299
|
+
return n === undefined ? null : { lineHeight: n };
|
|
300
|
+
}
|
|
301
|
+
case 'tracking': return TRACKING[v] !== undefined ? { letterSpacing: TRACKING[v] } : null;
|
|
302
|
+
case 'rounded': {
|
|
303
|
+
if (v in RADIUS)
|
|
304
|
+
return { borderRadius: RADIUS[v] };
|
|
305
|
+
const side = /^([trbl]|tl|tr|br|bl)-(.+)$/.exec(v);
|
|
306
|
+
if (side && side[2] in RADIUS)
|
|
307
|
+
return { [`border${CORNER[side[1]]}Radius`]: RADIUS[side[2]] };
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
case 'border': {
|
|
311
|
+
if (/^\d+$/.test(v))
|
|
312
|
+
return { borderWidth: +v };
|
|
313
|
+
const side = /^([trbl])(?:-(\d+))?$/.exec(v);
|
|
314
|
+
if (side)
|
|
315
|
+
return { [`border${SIDE[side[1]]}Width`]: side[2] ? +side[2] : 1 };
|
|
316
|
+
const col = color(v);
|
|
317
|
+
return col ? { borderColor: col } : null;
|
|
318
|
+
}
|
|
319
|
+
case 'opacity': return /^\d+$/.test(v) ? { opacity: +v / 100 } : null;
|
|
320
|
+
case 'z': return /^\d+$/.test(v) ? { zIndex: +v } : null;
|
|
321
|
+
case 'overflow': return { overflow: v };
|
|
322
|
+
case 'overflow-x': return { overflowX: v };
|
|
323
|
+
case 'overflow-y': return { overflowY: v };
|
|
324
|
+
case 'top':
|
|
325
|
+
case 'right':
|
|
326
|
+
case 'bottom':
|
|
327
|
+
case 'left': {
|
|
328
|
+
const n = v === 'auto' ? 'auto' : SPACE[v] ?? size(v);
|
|
329
|
+
return n === undefined ? null : { [head]: n };
|
|
330
|
+
}
|
|
331
|
+
case 'inset': {
|
|
332
|
+
const n = SPACE[v] ?? size(v);
|
|
333
|
+
return n === undefined ? null : { top: n, right: n, bottom: n, left: n };
|
|
334
|
+
}
|
|
335
|
+
case 'grid-cols': return /^\d+$/.test(v)
|
|
336
|
+
? { gridTemplateColumns: `repeat(${v}, minmax(0, 1fr))` } : null;
|
|
337
|
+
case 'grid-rows': return /^\d+$/.test(v)
|
|
338
|
+
? { gridTemplateRows: `repeat(${v}, minmax(0, 1fr))` } : null;
|
|
339
|
+
case 'col-span': return v === 'full'
|
|
340
|
+
? { gridColumn: '1 / -1' } : /^\d+$/.test(v) ? { gridColumn: `span ${v} / span ${v}` } : null;
|
|
341
|
+
case 'aspect': return v === 'square' ? { aspectRatio: 1 } : v === 'video' ? { aspectRatio: 16 / 9 } : null;
|
|
342
|
+
case 'object': return { objectFit: v };
|
|
343
|
+
case 'whitespace': return { whiteSpace: v };
|
|
344
|
+
case 'align': return { verticalAlign: v };
|
|
345
|
+
case 'transition': return TRANSITION[v] ? { transitionProperty: TRANSITION[v] } : null;
|
|
346
|
+
case 'duration': return DURATION[v] !== undefined ? { transitionDuration: `${DURATION[v]}ms` } : null;
|
|
347
|
+
case 'delay': return DURATION[v] !== undefined ? { transitionDelay: `${DURATION[v]}ms` } : null;
|
|
348
|
+
case 'blur': return BLUR[v] !== undefined ? { filter: `blur(${BLUR[v]}px)` } : null;
|
|
349
|
+
case 'backdrop-blur': return BLUR[v] !== undefined ? { backdropFilter: `blur(${BLUR[v]}px)` } : null;
|
|
350
|
+
// `space-y` puts the gap on the children's margins; `gap` puts it on the
|
|
351
|
+
// container. The rendering matches wherever the container is flex or grid,
|
|
352
|
+
// which is everywhere these classes are used, and `gap` is the one a stack
|
|
353
|
+
// already speaks.
|
|
354
|
+
case 'space-y': {
|
|
355
|
+
const n = SPACE[v] ?? size(v);
|
|
356
|
+
return n === undefined ? null : { rowGap: n };
|
|
357
|
+
}
|
|
358
|
+
case 'space-x': {
|
|
359
|
+
const n = SPACE[v] ?? size(v);
|
|
360
|
+
return n === undefined ? null : { columnGap: n };
|
|
361
|
+
}
|
|
362
|
+
case 'row-span': return /^\d+$/.test(v) ? { gridRow: `span ${v} / span ${v}` } : null;
|
|
363
|
+
case 'translate-x': {
|
|
364
|
+
const n = SPACE[v] ?? size(v);
|
|
365
|
+
return n === undefined ? null
|
|
366
|
+
: { transform: `translateX(${typeof n === 'number' ? n + 'px' : n})` };
|
|
367
|
+
}
|
|
368
|
+
case 'translate-y': {
|
|
369
|
+
const n = SPACE[v] ?? size(v);
|
|
370
|
+
return n === undefined ? null
|
|
371
|
+
: { transform: `translateY(${typeof n === 'number' ? n + 'px' : n})` };
|
|
372
|
+
}
|
|
373
|
+
case 'shadow': return SHADOW[v] ? { boxShadow: SHADOW[v] } : null;
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const MAX_W = {
|
|
378
|
+
none: 'none', xs: 320, sm: 384, md: 448, lg: 512, xl: 576, '2xl': 672,
|
|
379
|
+
'3xl': 768, '4xl': 896, '5xl': 1024, '6xl': 1152, '7xl': 1280, full: '100%',
|
|
380
|
+
prose: '65ch', screen: '100vw',
|
|
381
|
+
};
|
|
382
|
+
const CORNER = {
|
|
383
|
+
t: 'Top', r: 'Right', b: 'Bottom', l: 'Left',
|
|
384
|
+
tl: 'TopLeft', tr: 'TopRight', br: 'BottomRight', bl: 'BottomLeft',
|
|
385
|
+
};
|
|
386
|
+
/** Where a prefixed class lands: a gui media prop, or a pseudo-style. */
|
|
387
|
+
const VARIANT = {
|
|
388
|
+
sm: '$sm', md: '$md', lg: '$lg', xl: '$xl', '2xl': '$2xl',
|
|
389
|
+
hover: 'hoverStyle', focus: 'focusStyle', 'focus-visible': 'focusVisibleStyle',
|
|
390
|
+
active: 'pressStyle', disabled: 'disabledStyle', dark: '$theme-dark',
|
|
391
|
+
'group-hover': 'groupHoverStyle',
|
|
392
|
+
};
|
|
393
|
+
/** clsx's input shapes, since callers already write class names that way. */
|
|
394
|
+
function flatten(v, out = []) {
|
|
395
|
+
if (!v)
|
|
396
|
+
return out;
|
|
397
|
+
if (typeof v === 'string') {
|
|
398
|
+
for (const s of v.split(/\s+/))
|
|
399
|
+
if (s)
|
|
400
|
+
out.push(s);
|
|
401
|
+
return out;
|
|
402
|
+
}
|
|
403
|
+
if (typeof v === 'number') {
|
|
404
|
+
out.push(String(v));
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
if (Array.isArray(v)) {
|
|
408
|
+
for (const x of v)
|
|
409
|
+
flatten(x, out);
|
|
410
|
+
return out;
|
|
411
|
+
}
|
|
412
|
+
for (const k in v)
|
|
413
|
+
if (v[k])
|
|
414
|
+
out.push(k);
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* `tw('flex items-center gap-4 md:gap-8 hover:bg-muted')` becomes
|
|
419
|
+
* `{ display, flexDirection, alignItems, gap, $md: { gap }, hoverStyle: { backgroundColor } }`.
|
|
420
|
+
*/
|
|
421
|
+
function tw(input) {
|
|
422
|
+
const props = {};
|
|
423
|
+
const rest = [];
|
|
424
|
+
// A gradient is the one value spelled across several classes — a direction
|
|
425
|
+
// and its stops — so it is assembled here rather than in `one`, which sees
|
|
426
|
+
// a single class and could only ever produce a fragment of it.
|
|
427
|
+
const grad = {};
|
|
428
|
+
for (const raw of flatten(input)) {
|
|
429
|
+
// A class may carry several prefixes; the last one is the variant that
|
|
430
|
+
// decides where it lands, and an unknown prefix keeps the whole class.
|
|
431
|
+
const parts = raw.split(':');
|
|
432
|
+
const bare = parts.pop();
|
|
433
|
+
if (!parts.length) {
|
|
434
|
+
const g = /^(?:bg-gradient-to-([a-z]{1,2})|(from|via|to)-(.+))$/.exec(bare);
|
|
435
|
+
if (g) {
|
|
436
|
+
if (g[1])
|
|
437
|
+
grad.dir = DIRECTION[g[1]];
|
|
438
|
+
else
|
|
439
|
+
grad[g[2]] = color(g[3]);
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const found = one(bare);
|
|
444
|
+
if (!found) {
|
|
445
|
+
rest.push(raw);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (!parts.length) {
|
|
449
|
+
Object.assign(props, found);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const key = VARIANT[parts[parts.length - 1]];
|
|
453
|
+
if (!key) {
|
|
454
|
+
rest.push(raw);
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
const bucket = (props[key] ??= {});
|
|
458
|
+
Object.assign(bucket, found);
|
|
459
|
+
}
|
|
460
|
+
// A stop with no direction still reads as a gradient; Tailwind's own
|
|
461
|
+
// default is left-to-right.
|
|
462
|
+
const stops = [grad.from, grad.via, grad.to].filter(Boolean);
|
|
463
|
+
if (stops.length > 1) {
|
|
464
|
+
props.backgroundImage = `linear-gradient(${grad.dir ?? 'to right'}, ${stops.join(', ')})`;
|
|
465
|
+
}
|
|
466
|
+
return { props, rest: rest.join(' ') };
|
|
467
|
+
}
|
|
468
|
+
exports.default = tw;
|
package/dist/tw.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility class and a gui style prop are two notations for one value: `px-4`
|
|
3
|
+
* and `px={16}` say the same thing. This is the function between them.
|
|
4
|
+
*
|
|
5
|
+
* It exists so an app carrying a hundred thousand utility classes can render
|
|
6
|
+
* through gui without either rewriting every class first or keeping a second
|
|
7
|
+
* styling engine around to read them. Convert the notation, delete the engine.
|
|
8
|
+
*
|
|
9
|
+
* Values are CONCRETE — `p-4` is 16, not `$4`. The token scale is where these
|
|
10
|
+
* apps are going, but a token substitution moves every measurement on the page
|
|
11
|
+
* at the same time as the engine underneath it changes, and then nothing
|
|
12
|
+
* identifies which of the two moved it. Retheming is its own step.
|
|
13
|
+
*
|
|
14
|
+
* A class this does not know is returned in `rest`, never dropped. A caller
|
|
15
|
+
* passes it through as a class name, so an unconverted page degrades to what it
|
|
16
|
+
* does today rather than to an unstyled one.
|
|
17
|
+
*/
|
|
18
|
+
export type Props = Record<string, unknown>;
|
|
19
|
+
/** Recognized props, plus the classes that stay class names. */
|
|
20
|
+
export type Parsed = {
|
|
21
|
+
props: Props;
|
|
22
|
+
rest: string;
|
|
23
|
+
};
|
|
24
|
+
export type ClassValue = string | number | null | undefined | false | ClassValue[] | Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* `tw('flex items-center gap-4 md:gap-8 hover:bg-muted')` becomes
|
|
27
|
+
* `{ display, flexDirection, alignItems, gap, $md: { gap }, hoverStyle: { backgroundColor } }`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function tw(input: ClassValue): Parsed;
|
|
30
|
+
export default tw;
|
|
31
|
+
//# sourceMappingURL=tw.d.ts.map
|
package/dist/tw.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tw.d.ts","sourceRoot":"","sources":["../src/tw.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE3C,gEAAgE;AAChE,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAiUnD,MAAM,MAAM,UAAU,GAClB,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,GAC1C,UAAU,EAAE,GACZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAY3B;;;GAGG;AACH,wBAAgB,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAyC5C;AAED,eAAe,EAAE,CAAA"}
|