@stonedogcode/style 0.22.0 → 0.23.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/package.json +1 -1
- package/src/components/StyledBox.tsx +101 -0
package/package.json
CHANGED
|
@@ -101,6 +101,79 @@ export interface StyledBoxProps
|
|
|
101
101
|
scrollbar?: "auto" | "on" | "off";
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The layout props that describe how a caller wants THEIR CHILDREN arranged.
|
|
106
|
+
*
|
|
107
|
+
* Without `noWrap`, `StyledBox` puts children three levels down:
|
|
108
|
+
*
|
|
109
|
+
* ```
|
|
110
|
+
* StyledBoxRoot ← these props used to land here, and only here
|
|
111
|
+
* └ StyledVStack ← ...whose only child is this
|
|
112
|
+
* └ div
|
|
113
|
+
* └ StyledGridPanel
|
|
114
|
+
* └ div ← a plain block div, where the children actually live
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* So a root flex container laid out exactly ONE item and the caller's children
|
|
118
|
+
* stayed in ordinary block flow — `display="flex" flexDirection="column"` did
|
|
119
|
+
* nothing, `alignItems` centred a wrapper, and `gap` separated nothing.
|
|
120
|
+
*
|
|
121
|
+
* The visible cost, three times over: two adjacent `StyledText` siblings render
|
|
122
|
+
* as one welded run, because `StyledText` is a `<span>` and JSX strips the
|
|
123
|
+
* whitespace between elements on separate lines. Hopper Vitals shipped
|
|
124
|
+
* `264.2Weight` and `Sep 6Record another to see a trend.` (NEH-1473), which is
|
|
125
|
+
* the same symptom NEH-490 fixed twice at the call site. Fixing it at the call
|
|
126
|
+
* site is why it came back.
|
|
127
|
+
*
|
|
128
|
+
* **This cannot break anything that currently works.** These declarations were
|
|
129
|
+
* discarded before — a flex container with one child arranges nothing — so
|
|
130
|
+
* nothing can be depending on their effect. Same argument `StyledText`'s block
|
|
131
|
+
* promotion makes for vertical margins on an inline box.
|
|
132
|
+
*
|
|
133
|
+
* They are FORWARDED, not moved: the root keeps them too, so a caller relying
|
|
134
|
+
* on the root's own box (a `gap` between header, content and footer, say) is
|
|
135
|
+
* unaffected.
|
|
136
|
+
*
|
|
137
|
+
* Sizing props are deliberately absent — `width`, `height`, `padding`,
|
|
138
|
+
* `margin`, `overflow` and friends genuinely apply to the root's own box, and
|
|
139
|
+
* copying them inward would double padding and re-clip content.
|
|
140
|
+
*/
|
|
141
|
+
const LAYOUT_PROPS = [
|
|
142
|
+
"display",
|
|
143
|
+
"flexDirection",
|
|
144
|
+
"flexWrap",
|
|
145
|
+
"alignItems",
|
|
146
|
+
"alignContent",
|
|
147
|
+
"justifyContent",
|
|
148
|
+
"justifyItems",
|
|
149
|
+
"gap",
|
|
150
|
+
"rowGap",
|
|
151
|
+
"columnGap",
|
|
152
|
+
"gridTemplateColumns",
|
|
153
|
+
"gridTemplateRows",
|
|
154
|
+
"gridAutoFlow",
|
|
155
|
+
"placeItems",
|
|
156
|
+
"placeContent",
|
|
157
|
+
] as const;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Did the caller ask for a layout of their own?
|
|
161
|
+
*
|
|
162
|
+
* When they did, and nothing else needs the wrapper, `StyledBox` renders the
|
|
163
|
+
* children DIRECTLY inside the root so the root really is their parent — which
|
|
164
|
+
* is what makes every Panda prop work, `gap` included.
|
|
165
|
+
*
|
|
166
|
+
* Forwarding the props as inline CSS was tried first and is wrong: `gap="2"` is
|
|
167
|
+
* a Panda spacing TOKEN, not a length, so `style={{ gap: "2" }}` is invalid CSS
|
|
168
|
+
* and silently dropped. Panda resolves tokens at build time for props on a
|
|
169
|
+
* styled component; it cannot for a runtime value written into a style
|
|
170
|
+
* attribute. Making the root the parent sidesteps the problem entirely — the props
|
|
171
|
+
* stay exactly where Panda already handles them.
|
|
172
|
+
*/
|
|
173
|
+
function wantsOwnLayout(rest: Record<string, unknown>): boolean {
|
|
174
|
+
return LAYOUT_PROPS.some((prop) => rest[prop] !== undefined && rest[prop] !== null);
|
|
175
|
+
}
|
|
176
|
+
|
|
104
177
|
const StyledBoxRoot = styled("div", boxRecipe);
|
|
105
178
|
|
|
106
179
|
const StyledBox = React.forwardRef<HTMLDivElement, StyledBoxProps>(
|
|
@@ -137,6 +210,10 @@ const StyledBox = React.forwardRef<HTMLDivElement, StyledBoxProps>(
|
|
|
137
210
|
// scrollbar prop controls inner content overflow
|
|
138
211
|
const innerOverflow: Property.Overflow = scrollbar === "auto" ? "auto" : scrollbar === "on" ? "scroll" : "hidden";
|
|
139
212
|
|
|
213
|
+
// Does the caller lay out their own children? If so the wrapper is what
|
|
214
|
+
// stands between their props and the elements those props describe.
|
|
215
|
+
const callerLaysOutChildren = wantsOwnLayout(rest as Record<string, unknown>);
|
|
216
|
+
|
|
140
217
|
if (noWrap) {
|
|
141
218
|
if (resolvedHeader || resolvedFooter) {
|
|
142
219
|
return (
|
|
@@ -153,6 +230,30 @@ const StyledBox = React.forwardRef<HTMLDivElement, StyledBoxProps>(
|
|
|
153
230
|
</StyledBoxRoot>
|
|
154
231
|
);
|
|
155
232
|
}
|
|
233
|
+
/*
|
|
234
|
+
The caller lays out their own children and nothing else needs the
|
|
235
|
+
wrapper, so the root parents them directly.
|
|
236
|
+
|
|
237
|
+
Gated on there being no header, footer, panels or scrollbar: each of
|
|
238
|
+
those is a real reason the wrapper exists, and this must not take any of
|
|
239
|
+
them away. What is left is the case where the wrapper only ever stood
|
|
240
|
+
between a caller's layout props and the children they describe.
|
|
241
|
+
*/
|
|
242
|
+
if (
|
|
243
|
+
callerLaysOutChildren &&
|
|
244
|
+
!resolvedHeader &&
|
|
245
|
+
!resolvedFooter &&
|
|
246
|
+
!leftPanel &&
|
|
247
|
+
!rightPanel &&
|
|
248
|
+
scrollbar === undefined
|
|
249
|
+
) {
|
|
250
|
+
return (
|
|
251
|
+
<StyledBoxRoot ref={ref} {...(as ? { as } : {})} className={combinedClassName} {...rest}>
|
|
252
|
+
{children}
|
|
253
|
+
</StyledBoxRoot>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
156
257
|
return (
|
|
157
258
|
<StyledBoxRoot ref={ref} {...(as ? { as } : {})} className={combinedClassName} {...rest}>
|
|
158
259
|
<StyledVStack gap="0" width="100%" height="100%" style={{ overflow: innerOverflow }}>
|