@epoch32/visage 0.0.0-stage → 1.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/LICENSE +373 -0
- package/README.md +110 -2
- package/dist/src/api/index.js +4002 -0
- package/dist/src/charts/index.js +2717 -0
- package/dist/src/devtools/index.js +4677 -0
- package/dist/src/dom/index.js +1550 -0
- package/dist/src/form/index.js +1415 -0
- package/dist/src/index.js +11826 -0
- package/dist/src/jsx-runtime/index.js +1630 -0
- package/dist/src/query/index.js +2422 -0
- package/dist/src/router/index.js +1856 -0
- package/dist/src/rpc/index.js +3484 -0
- package/dist/src/server/index.js +1173 -0
- package/dist/src/signals/index.js +1077 -0
- package/dist/src/style/index.js +2340 -0
- package/dist/src/template/index.js +1149 -0
- package/dist/src/ui/index.js +4155 -0
- package/dist/src/virtual/index.js +1955 -0
- package/dist/src/vite/index.js +984 -0
- package/package.json +140 -4
- package/src/api/client.ts +371 -0
- package/src/api/file-router.ts +183 -0
- package/src/api/handler.ts +326 -0
- package/src/api/index.ts +71 -0
- package/src/api/middleware.ts +54 -0
- package/src/api/pragma.ts +155 -0
- package/src/api/procedure.ts +172 -0
- package/src/api/types.ts +249 -0
- package/src/charts/Area.tsx +111 -0
- package/src/charts/Axis.tsx +126 -0
- package/src/charts/Bar.tsx +86 -0
- package/src/charts/CartesianGrid.tsx +72 -0
- package/src/charts/Chart.tsx +269 -0
- package/src/charts/Cursor.tsx +42 -0
- package/src/charts/Legend.tsx +56 -0
- package/src/charts/Line.tsx +106 -0
- package/src/charts/Pie.tsx +88 -0
- package/src/charts/Tooltip.tsx +127 -0
- package/src/charts/context.ts +30 -0
- package/src/charts/index.ts +14 -0
- package/src/charts/path.ts +155 -0
- package/src/charts/scale.ts +133 -0
- package/src/charts/types.ts +50 -0
- package/src/devtools/DomTab.tsx +134 -0
- package/src/devtools/GraphInspector.tsx +196 -0
- package/src/devtools/GraphTab.tsx +315 -0
- package/src/devtools/QueryTab.tsx +200 -0
- package/src/devtools/RpcTab.tsx +190 -0
- package/src/devtools/SignalsTab.tsx +129 -0
- package/src/devtools/component.tsx +243 -0
- package/src/devtools/highlighter.ts +134 -0
- package/src/devtools/hub.ts +175 -0
- package/src/devtools/index.ts +20 -0
- package/src/devtools/styles.ts +313 -0
- package/src/devtools/types.ts +45 -0
- package/src/dom/async.ts +232 -0
- package/src/dom/control-flow.ts +353 -0
- package/src/dom/index.ts +32 -0
- package/src/dom/lazy.ts +82 -0
- package/src/dom/render.ts +286 -0
- package/src/dom/resource.ts +120 -0
- package/src/form/form.ts +218 -0
- package/src/form/index.ts +17 -0
- package/src/form/schema.ts +299 -0
- package/src/index.ts +413 -0
- package/src/jsx-dev-runtime/index.ts +1 -0
- package/src/jsx-runtime/index.ts +112 -0
- package/src/jsx-runtime/jsx.d.ts +278 -0
- package/src/query/create-mutation.ts +64 -0
- package/src/query/create-query.ts +426 -0
- package/src/query/focus-manager.ts +68 -0
- package/src/query/index.ts +63 -0
- package/src/query/mutation-cache.ts +138 -0
- package/src/query/online-manager.ts +67 -0
- package/src/query/query-cache.ts +369 -0
- package/src/query/query-client.ts +248 -0
- package/src/query/types.ts +221 -0
- package/src/query/utils.ts +132 -0
- package/src/router/index.ts +19 -0
- package/src/router/router.tsx +352 -0
- package/src/rpc/client.ts +317 -0
- package/src/rpc/error.ts +70 -0
- package/src/rpc/index.ts +52 -0
- package/src/rpc/procedure.ts +88 -0
- package/src/rpc/router.ts +105 -0
- package/src/rpc/server.ts +511 -0
- package/src/rpc/types.ts +328 -0
- package/src/server/hydrate.ts +21 -0
- package/src/server/index.ts +10 -0
- package/src/server/render-to-stream.ts +31 -0
- package/src/server/render-to-string.ts +125 -0
- package/src/signals/computed.ts +217 -0
- package/src/signals/context.ts +89 -0
- package/src/signals/effect.ts +161 -0
- package/src/signals/index.ts +57 -0
- package/src/signals/scope.ts +132 -0
- package/src/signals/signal.ts +203 -0
- package/src/signals/store.ts +163 -0
- package/src/signals/system.ts +124 -0
- package/src/signals/types.ts +64 -0
- package/src/style/component.tsx +122 -0
- package/src/style/create-style.ts +229 -0
- package/src/style/cx.ts +75 -0
- package/src/style/hash.ts +19 -0
- package/src/style/index.ts +8 -0
- package/src/style/layout.tsx +406 -0
- package/src/style/manager.ts +87 -0
- package/src/style/parser.ts +257 -0
- package/src/style/scale.ts +237 -0
- package/src/template/index.ts +4 -0
- package/src/template/template.ts +126 -0
- package/src/ui/accordion.tsx +286 -0
- package/src/ui/command-dialog.tsx +49 -0
- package/src/ui/command.tsx +400 -0
- package/src/ui/data-table.tsx +322 -0
- package/src/ui/dialog.tsx +362 -0
- package/src/ui/dropdown-menu.tsx +381 -0
- package/src/ui/icon.tsx +69 -0
- package/src/ui/index.ts +138 -0
- package/src/ui/layout.tsx +13 -0
- package/src/ui/popover.tsx +249 -0
- package/src/ui/switch.tsx +114 -0
- package/src/ui/tabs.tsx +297 -0
- package/src/ui/toast.tsx +280 -0
- package/src/ui/tooltip.tsx +262 -0
- package/src/ui/utils.ts +110 -0
- package/src/virtual/index.ts +12 -0
- package/src/virtual/virtual-list.tsx +185 -0
- package/src/virtual/virtualizer.ts +274 -0
- package/src/vite/hmr.ts +38 -0
- package/src/vite/index.ts +3 -0
- package/src/vite/lucide.ts +124 -0
- package/src/vite/plugin.ts +102 -0
- package/src/vite/rpc-middleware.ts +82 -0
- package/src/vite/types.ts +65 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import type { Child } from "../dom";
|
|
2
|
+
import { jsx } from "../jsx-runtime";
|
|
3
|
+
import type { ClassProp, HTMLAttributes } from "../jsx-runtime/jsx";
|
|
4
|
+
import { resolveDimension, space } from "./scale";
|
|
5
|
+
|
|
6
|
+
const ALIGN_MAP: Record<string, string> = {
|
|
7
|
+
start: "flex-start",
|
|
8
|
+
center: "center",
|
|
9
|
+
end: "flex-end",
|
|
10
|
+
stretch: "stretch",
|
|
11
|
+
baseline: "baseline"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const JUSTIFY_MAP: Record<string, string> = {
|
|
15
|
+
start: "flex-start",
|
|
16
|
+
center: "center",
|
|
17
|
+
end: "flex-end",
|
|
18
|
+
between: "space-between",
|
|
19
|
+
around: "space-around",
|
|
20
|
+
evenly: "space-evenly",
|
|
21
|
+
stretch: "stretch"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface BaseLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
25
|
+
as?: string;
|
|
26
|
+
gap?: number | string;
|
|
27
|
+
p?: number | string;
|
|
28
|
+
px?: number | string;
|
|
29
|
+
py?: number | string;
|
|
30
|
+
pt?: number | string;
|
|
31
|
+
pb?: number | string;
|
|
32
|
+
pl?: number | string;
|
|
33
|
+
pr?: number | string;
|
|
34
|
+
m?: number | string;
|
|
35
|
+
mx?: number | string;
|
|
36
|
+
my?: number | string;
|
|
37
|
+
mt?: number | string;
|
|
38
|
+
mb?: number | string;
|
|
39
|
+
ml?: number | string;
|
|
40
|
+
mr?: number | string;
|
|
41
|
+
w?: number | string;
|
|
42
|
+
h?: number | string;
|
|
43
|
+
minW?: number | string;
|
|
44
|
+
maxW?: number | string;
|
|
45
|
+
minH?: number | string;
|
|
46
|
+
maxH?: number | string;
|
|
47
|
+
class?: ClassProp;
|
|
48
|
+
className?: ClassProp;
|
|
49
|
+
style?: any;
|
|
50
|
+
children?: Child;
|
|
51
|
+
[key: string]: any;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveBaseStyles(props: BaseLayoutProps): Record<string, string> {
|
|
55
|
+
const s: Record<string, string> = {
|
|
56
|
+
boxSizing: "border-box"
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (props.gap !== undefined) s.gap = space(props.gap);
|
|
60
|
+
if (props.p !== undefined) s.padding = space(props.p);
|
|
61
|
+
if (props.px !== undefined) {
|
|
62
|
+
s.paddingLeft = space(props.px);
|
|
63
|
+
s.paddingRight = space(props.px);
|
|
64
|
+
}
|
|
65
|
+
if (props.py !== undefined) {
|
|
66
|
+
s.paddingTop = space(props.py);
|
|
67
|
+
s.paddingBottom = space(props.py);
|
|
68
|
+
}
|
|
69
|
+
if (props.pt !== undefined) s.paddingTop = space(props.pt);
|
|
70
|
+
if (props.pb !== undefined) s.paddingBottom = space(props.pb);
|
|
71
|
+
if (props.pl !== undefined) s.paddingLeft = space(props.pl);
|
|
72
|
+
if (props.pr !== undefined) s.paddingRight = space(props.pr);
|
|
73
|
+
|
|
74
|
+
if (props.m !== undefined) s.margin = space(props.m);
|
|
75
|
+
if (props.mx !== undefined) {
|
|
76
|
+
s.marginLeft = space(props.mx);
|
|
77
|
+
s.marginRight = space(props.mx);
|
|
78
|
+
}
|
|
79
|
+
if (props.my !== undefined) {
|
|
80
|
+
s.marginTop = space(props.my);
|
|
81
|
+
s.marginBottom = space(props.my);
|
|
82
|
+
}
|
|
83
|
+
if (props.mt !== undefined) s.marginTop = space(props.mt);
|
|
84
|
+
if (props.mb !== undefined) s.marginBottom = space(props.mb);
|
|
85
|
+
if (props.ml !== undefined) s.marginLeft = space(props.ml);
|
|
86
|
+
if (props.mr !== undefined) s.marginRight = space(props.mr);
|
|
87
|
+
|
|
88
|
+
if (props.w !== undefined) s.width = resolveDimension(props.w);
|
|
89
|
+
if (props.h !== undefined) s.height = resolveDimension(props.h);
|
|
90
|
+
if (props.minW !== undefined) s.minWidth = resolveDimension(props.minW);
|
|
91
|
+
if (props.maxW !== undefined) s.maxWidth = resolveDimension(props.maxW);
|
|
92
|
+
if (props.minH !== undefined) s.minHeight = resolveDimension(props.minH);
|
|
93
|
+
if (props.maxH !== undefined) s.maxHeight = resolveDimension(props.maxH);
|
|
94
|
+
|
|
95
|
+
return s;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function mergeStyles(base: Record<string, string>, userStyle: any): any {
|
|
99
|
+
if (!userStyle) return base;
|
|
100
|
+
if (typeof userStyle === "string") {
|
|
101
|
+
const baseStr = Object.entries(base)
|
|
102
|
+
.map(([k, v]) => `${k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`)
|
|
103
|
+
.join("; ");
|
|
104
|
+
return `${baseStr}; ${userStyle}`;
|
|
105
|
+
}
|
|
106
|
+
if (typeof userStyle === "object") {
|
|
107
|
+
return { ...base, ...userStyle };
|
|
108
|
+
}
|
|
109
|
+
return base;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Strip layout-specific helper props so they are not set as DOM attributes
|
|
113
|
+
function cleanProps(props: Record<string, any>, excludeKeys: Set<string>): Record<string, any> {
|
|
114
|
+
const result: Record<string, any> = {};
|
|
115
|
+
for (const k in props) {
|
|
116
|
+
if (!excludeKeys.has(k)) {
|
|
117
|
+
result[k] = props[k];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const COMMON_EXCLUDE = new Set([
|
|
124
|
+
"as",
|
|
125
|
+
"gap",
|
|
126
|
+
"p",
|
|
127
|
+
"px",
|
|
128
|
+
"py",
|
|
129
|
+
"pt",
|
|
130
|
+
"pb",
|
|
131
|
+
"pl",
|
|
132
|
+
"pr",
|
|
133
|
+
"m",
|
|
134
|
+
"mx",
|
|
135
|
+
"my",
|
|
136
|
+
"mt",
|
|
137
|
+
"mb",
|
|
138
|
+
"ml",
|
|
139
|
+
"mr",
|
|
140
|
+
"w",
|
|
141
|
+
"h",
|
|
142
|
+
"minW",
|
|
143
|
+
"maxW",
|
|
144
|
+
"minH",
|
|
145
|
+
"maxH"
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
export interface StackProps extends BaseLayoutProps {
|
|
149
|
+
align?: "start" | "center" | "end" | "stretch" | "baseline";
|
|
150
|
+
justify?: "start" | "center" | "end" | "between" | "around" | "evenly";
|
|
151
|
+
direction?: "vertical" | "horizontal";
|
|
152
|
+
reversed?: boolean;
|
|
153
|
+
wrap?: boolean | "wrap" | "nowrap" | "wrap-reverse";
|
|
154
|
+
inline?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const STACK_EXCLUDE = new Set([
|
|
158
|
+
...COMMON_EXCLUDE,
|
|
159
|
+
"align",
|
|
160
|
+
"justify",
|
|
161
|
+
"direction",
|
|
162
|
+
"reversed",
|
|
163
|
+
"wrap",
|
|
164
|
+
"inline"
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* <Stack> Layout Primitive
|
|
169
|
+
*
|
|
170
|
+
* Vertical (column) layout by default. Provides zero margin collapse,
|
|
171
|
+
* built-in spatial scale gap, and automatic flex overflow containment.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* <Stack gap={4} align="center">
|
|
175
|
+
* <Header />
|
|
176
|
+
* <Content />
|
|
177
|
+
* </Stack>
|
|
178
|
+
*/
|
|
179
|
+
export function Stack(props: StackProps): Child {
|
|
180
|
+
const { as = "div", children, ...rest } = props;
|
|
181
|
+
const layoutStyles = resolveBaseStyles(props);
|
|
182
|
+
|
|
183
|
+
layoutStyles.display = props.inline ? "inline-flex" : "flex";
|
|
184
|
+
layoutStyles.flexDirection =
|
|
185
|
+
props.direction === "horizontal"
|
|
186
|
+
? props.reversed
|
|
187
|
+
? "row-reverse"
|
|
188
|
+
: "row"
|
|
189
|
+
: props.reversed
|
|
190
|
+
? "column-reverse"
|
|
191
|
+
: "column";
|
|
192
|
+
|
|
193
|
+
if (props.align && ALIGN_MAP[props.align]) {
|
|
194
|
+
layoutStyles.alignItems = ALIGN_MAP[props.align]!;
|
|
195
|
+
}
|
|
196
|
+
if (props.justify && JUSTIFY_MAP[props.justify]) {
|
|
197
|
+
layoutStyles.justifyContent = JUSTIFY_MAP[props.justify]!;
|
|
198
|
+
}
|
|
199
|
+
if (props.wrap) {
|
|
200
|
+
layoutStyles.flexWrap = props.wrap === true ? "wrap" : props.wrap;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const domProps = cleanProps(rest, STACK_EXCLUDE);
|
|
204
|
+
domProps.style = mergeStyles(layoutStyles, props.style);
|
|
205
|
+
domProps.children = children;
|
|
206
|
+
|
|
207
|
+
return jsx(as, domProps);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface RowProps extends BaseLayoutProps {
|
|
211
|
+
align?: "start" | "center" | "end" | "stretch" | "baseline";
|
|
212
|
+
justify?: "start" | "center" | "end" | "between" | "around" | "evenly";
|
|
213
|
+
reversed?: boolean;
|
|
214
|
+
wrap?: boolean | "wrap" | "nowrap" | "wrap-reverse";
|
|
215
|
+
inline?: boolean;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const ROW_EXCLUDE = new Set([...COMMON_EXCLUDE, "align", "justify", "reversed", "wrap", "inline"]);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* <Row> Layout Primitive
|
|
222
|
+
*
|
|
223
|
+
* Horizontal row layout with sensible vertical centering (align="center") by default.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* <Row gap={3} justify="between">
|
|
227
|
+
* <Avatar />
|
|
228
|
+
* <Typography>Online</Typography>
|
|
229
|
+
* </Row>
|
|
230
|
+
*/
|
|
231
|
+
export function Row(props: RowProps): Child {
|
|
232
|
+
const { as = "div", children, ...rest } = props;
|
|
233
|
+
const layoutStyles = resolveBaseStyles(props);
|
|
234
|
+
|
|
235
|
+
layoutStyles.display = props.inline ? "inline-flex" : "flex";
|
|
236
|
+
layoutStyles.flexDirection = props.reversed ? "row-reverse" : "row";
|
|
237
|
+
layoutStyles.alignItems = props.align ? (ALIGN_MAP[props.align] ?? props.align) : "center";
|
|
238
|
+
|
|
239
|
+
if (props.justify && JUSTIFY_MAP[props.justify]) {
|
|
240
|
+
layoutStyles.justifyContent = JUSTIFY_MAP[props.justify]!;
|
|
241
|
+
}
|
|
242
|
+
if (props.wrap) {
|
|
243
|
+
layoutStyles.flexWrap = props.wrap === true ? "wrap" : props.wrap;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const domProps = cleanProps(rest, ROW_EXCLUDE);
|
|
247
|
+
domProps.style = mergeStyles(layoutStyles, props.style);
|
|
248
|
+
domProps.children = children;
|
|
249
|
+
|
|
250
|
+
return jsx(as, domProps);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface CenterProps extends BaseLayoutProps {
|
|
254
|
+
inline?: boolean;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const CENTER_EXCLUDE = new Set([...COMMON_EXCLUDE, "inline"]);
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* <Center> Layout Primitive
|
|
261
|
+
*
|
|
262
|
+
* Centers child content horizontally and vertically without CSS boilerplate.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* <Center h="screen">
|
|
266
|
+
* <Spinner />
|
|
267
|
+
* </Center>
|
|
268
|
+
*/
|
|
269
|
+
export function Center(props: CenterProps): Child {
|
|
270
|
+
const { as = "div", children, ...rest } = props;
|
|
271
|
+
const layoutStyles = resolveBaseStyles(props);
|
|
272
|
+
|
|
273
|
+
layoutStyles.display = props.inline ? "inline-flex" : "flex";
|
|
274
|
+
layoutStyles.alignItems = "center";
|
|
275
|
+
layoutStyles.justifyContent = "center";
|
|
276
|
+
|
|
277
|
+
const domProps = cleanProps(rest, CENTER_EXCLUDE);
|
|
278
|
+
domProps.style = mergeStyles(layoutStyles, props.style);
|
|
279
|
+
domProps.children = children;
|
|
280
|
+
|
|
281
|
+
return jsx(as, domProps);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface GridProps extends BaseLayoutProps {
|
|
285
|
+
columns?: number | string;
|
|
286
|
+
minChildWidth?: number | string;
|
|
287
|
+
rows?: number | string;
|
|
288
|
+
rowGap?: number | string;
|
|
289
|
+
colGap?: number | string;
|
|
290
|
+
align?: "start" | "center" | "end" | "stretch";
|
|
291
|
+
justify?: "start" | "center" | "end" | "stretch";
|
|
292
|
+
inline?: boolean;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const GRID_EXCLUDE = new Set([
|
|
296
|
+
...COMMON_EXCLUDE,
|
|
297
|
+
"columns",
|
|
298
|
+
"minChildWidth",
|
|
299
|
+
"rows",
|
|
300
|
+
"rowGap",
|
|
301
|
+
"colGap",
|
|
302
|
+
"align",
|
|
303
|
+
"justify",
|
|
304
|
+
"inline"
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* <Grid> Layout Primitive
|
|
309
|
+
*
|
|
310
|
+
* Effortless CSS Grid. Specify fixed columns or a responsive minChildWidth
|
|
311
|
+
* to achieve fluid wrapping grids without writing complex media queries.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* // Responsive auto-wrapping grid
|
|
315
|
+
* <Grid minChildWidth={260} gap={4}>
|
|
316
|
+
* <Card />
|
|
317
|
+
* <Card />
|
|
318
|
+
* </Grid>
|
|
319
|
+
*/
|
|
320
|
+
export function Grid(props: GridProps): Child {
|
|
321
|
+
const { as = "div", children, ...rest } = props;
|
|
322
|
+
const layoutStyles = resolveBaseStyles(props);
|
|
323
|
+
|
|
324
|
+
layoutStyles.display = props.inline ? "inline-grid" : "grid";
|
|
325
|
+
|
|
326
|
+
if (props.minChildWidth !== undefined) {
|
|
327
|
+
const minW = resolveDimension(props.minChildWidth);
|
|
328
|
+
layoutStyles.gridTemplateColumns = `repeat(auto-fit, minmax(min(100%, ${minW}), 1fr))`;
|
|
329
|
+
} else if (props.columns !== undefined) {
|
|
330
|
+
if (typeof props.columns === "number") {
|
|
331
|
+
layoutStyles.gridTemplateColumns = `repeat(${props.columns}, minmax(0, 1fr))`;
|
|
332
|
+
} else {
|
|
333
|
+
layoutStyles.gridTemplateColumns = String(props.columns);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (props.rows !== undefined) {
|
|
338
|
+
if (typeof props.rows === "number") {
|
|
339
|
+
layoutStyles.gridTemplateRows = `repeat(${props.rows}, minmax(0, 1fr))`;
|
|
340
|
+
} else {
|
|
341
|
+
layoutStyles.gridTemplateRows = String(props.rows);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (props.rowGap !== undefined) layoutStyles.rowGap = space(props.rowGap);
|
|
346
|
+
if (props.colGap !== undefined) layoutStyles.columnGap = space(props.colGap);
|
|
347
|
+
|
|
348
|
+
if (props.align) {
|
|
349
|
+
layoutStyles.alignItems = ALIGN_MAP[props.align] ?? props.align;
|
|
350
|
+
}
|
|
351
|
+
if (props.justify) {
|
|
352
|
+
layoutStyles.justifyItems = props.justify;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const domProps = cleanProps(rest, GRID_EXCLUDE);
|
|
356
|
+
domProps.style = mergeStyles(layoutStyles, props.style);
|
|
357
|
+
domProps.children = children;
|
|
358
|
+
|
|
359
|
+
return jsx(as, domProps);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export interface SpacerProps extends BaseLayoutProps {
|
|
363
|
+
size?: number | string;
|
|
364
|
+
x?: number | string;
|
|
365
|
+
y?: number | string;
|
|
366
|
+
flex?: boolean | number;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const SPACER_EXCLUDE = new Set([...COMMON_EXCLUDE, "size", "x", "y", "flex"]);
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* <Spacer> Layout Primitive
|
|
373
|
+
*
|
|
374
|
+
* Creates flexible pushing space in Rows/Stacks (defaults to flex: 1)
|
|
375
|
+
* or creates a dedicated spatial gap block when given size.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* <Row>
|
|
379
|
+
* <Logo />
|
|
380
|
+
* <Spacer />
|
|
381
|
+
* <UserProfile />
|
|
382
|
+
* </Row>
|
|
383
|
+
*/
|
|
384
|
+
export function Spacer(props: SpacerProps): Child {
|
|
385
|
+
const { as = "div", ...rest } = props;
|
|
386
|
+
const layoutStyles = resolveBaseStyles(props);
|
|
387
|
+
|
|
388
|
+
if (props.size !== undefined) {
|
|
389
|
+
const s = space(props.size);
|
|
390
|
+
layoutStyles.width = s;
|
|
391
|
+
layoutStyles.height = s;
|
|
392
|
+
layoutStyles.flexShrink = "0";
|
|
393
|
+
} else if (props.x !== undefined || props.y !== undefined) {
|
|
394
|
+
if (props.x !== undefined) layoutStyles.width = space(props.x);
|
|
395
|
+
if (props.y !== undefined) layoutStyles.height = space(props.y);
|
|
396
|
+
layoutStyles.flexShrink = "0";
|
|
397
|
+
} else {
|
|
398
|
+
layoutStyles.flex = typeof props.flex === "number" ? String(props.flex) : "1 1 0%";
|
|
399
|
+
layoutStyles.alignSelf = "stretch";
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const domProps = cleanProps(rest, SPACER_EXCLUDE);
|
|
403
|
+
domProps.style = mergeStyles(layoutStyles, props.style);
|
|
404
|
+
|
|
405
|
+
return jsx(as, domProps);
|
|
406
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface InjectedStyle {
|
|
2
|
+
id: string;
|
|
3
|
+
css: string;
|
|
4
|
+
count: number;
|
|
5
|
+
element?: HTMLStyleElement;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class StyleManager {
|
|
9
|
+
private _styles = new Map<string, InjectedStyle>();
|
|
10
|
+
private _ssrStyles = new Set<string>();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Inject CSS into the document head or SSR registry
|
|
14
|
+
*/
|
|
15
|
+
inject(id: string, css: string): () => void {
|
|
16
|
+
let entry = this._styles.get(id);
|
|
17
|
+
|
|
18
|
+
if (!entry) {
|
|
19
|
+
entry = { id, css, count: 0 };
|
|
20
|
+
this._styles.set(id, entry);
|
|
21
|
+
|
|
22
|
+
if (typeof document !== "undefined") {
|
|
23
|
+
const target = document.head || document.documentElement || document.body;
|
|
24
|
+
if (target) {
|
|
25
|
+
const styleEl = document.createElement("style");
|
|
26
|
+
styleEl.id = `visage-style-${id}`;
|
|
27
|
+
styleEl.setAttribute("data-visage-scope", id);
|
|
28
|
+
styleEl.textContent = css;
|
|
29
|
+
target.appendChild(styleEl);
|
|
30
|
+
entry.element = styleEl;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
this._ssrStyles.add(css);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
entry.count++;
|
|
37
|
+
|
|
38
|
+
// Return cleanup callback
|
|
39
|
+
return () => {
|
|
40
|
+
if (!entry) return;
|
|
41
|
+
entry.count--;
|
|
42
|
+
if (entry.count <= 0) {
|
|
43
|
+
if (entry.element?.parentNode) {
|
|
44
|
+
entry.element.parentNode.removeChild(entry.element);
|
|
45
|
+
}
|
|
46
|
+
this._styles.delete(id);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a style scope is currently registered
|
|
53
|
+
*/
|
|
54
|
+
has(id: string): boolean {
|
|
55
|
+
return this._styles.has(id);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Extract all styles collected during Server-Side Rendering (SSR)
|
|
60
|
+
*/
|
|
61
|
+
extractSSRStyles(): string {
|
|
62
|
+
return Array.from(this._ssrStyles).join("\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Clear all registered and SSR styles
|
|
67
|
+
*/
|
|
68
|
+
clear() {
|
|
69
|
+
for (const entry of this._styles.values()) {
|
|
70
|
+
if (entry.element?.parentNode) {
|
|
71
|
+
entry.element.parentNode.removeChild(entry.element);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this._styles.clear();
|
|
75
|
+
this._ssrStyles.clear();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const styleManager = new StyleManager();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Helper to get SSR style tag HTML
|
|
83
|
+
*/
|
|
84
|
+
export function extractStyleTags(): string {
|
|
85
|
+
const css = styleManager.extractSSRStyles();
|
|
86
|
+
return css ? `<style data-visage-ssr="true">${css}</style>` : "";
|
|
87
|
+
}
|