@purple-brock/burrow-web 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +13 -2
- package/dist/index.d.mts +116 -0
- package/dist/index.mjs +330 -0
- package/package.json +49 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luke Price (The Purple Brock)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/lrprice79/badger-burrow/HEAD/assets/purple-brock-icon.png" width="120" alt="BadgerBurrow">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# @purple-brock/burrow-web
|
|
6
|
+
|
|
7
|
+
BadgerBurrow for React on the web — `<Chamber>` and `<Tunnel>` with pseudo states (`_hover`, `_focus`…), responsive props and SSR style collection.
|
|
8
|
+
|
|
9
|
+
**Needs:** React ≥ 18. ESM only.
|
|
10
|
+
|
|
11
|
+
Part of **BadgerBurrow** by The Purple Brock — see the
|
|
12
|
+
[main README and usage guide](https://github.com/lrprice79/badger-burrow#readme).
|
|
13
|
+
|
|
14
|
+
MIT © 2026 Luke Price (The Purple Brock)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { BurrowSnapshot, ColorMode, BurrowBoxProps, BurrowTextProps } from '@purple-brock/burrow-core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface UseBurrowResult extends BurrowSnapshot {
|
|
5
|
+
setMode: (mode: ColorMode) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Returns the current BadgerBurrow token snapshot and a setMode helper.
|
|
9
|
+
*
|
|
10
|
+
* Subscribes to BadgerBase via useSyncExternalStore — renders only when
|
|
11
|
+
* tokens or mode actually change. No ThemeProvider required anywhere in
|
|
12
|
+
* the tree.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* function MyButton() {
|
|
16
|
+
* const { tokens, mode, setMode } = useBurrow()
|
|
17
|
+
* return (
|
|
18
|
+
* <button style={{ background: tokens.primitive.color.primary[500] }}>
|
|
19
|
+
* Switch to {mode === 'light' ? 'dark' : 'light'}
|
|
20
|
+
* </button>
|
|
21
|
+
* )
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
declare function useBurrow(): UseBurrowResult;
|
|
25
|
+
|
|
26
|
+
type ChamberTag = 'div' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'main' | 'nav' | 'button' | 'a' | 'form' | 'fieldset' | 'ul' | 'ol' | 'li' | 'figure' | 'figcaption' | 'label' | 'dialog' | 'details' | 'summary' | 'span';
|
|
27
|
+
interface ChamberProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, BurrowBoxProps {
|
|
28
|
+
/** Element tag to render — defaults to div */
|
|
29
|
+
as?: ChamberTag;
|
|
30
|
+
/** Required when as="a" */
|
|
31
|
+
href?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A styled box component backed by BadgerBurrow tokens.
|
|
35
|
+
* Accepts shorthand token props alongside all standard HTML props.
|
|
36
|
+
* Use `as` to render as any semantic element — button, nav, section, etc.
|
|
37
|
+
*
|
|
38
|
+
* Priority (lowest → highest): sx → named props → style
|
|
39
|
+
*
|
|
40
|
+
* Pseudo-state styling via _hover, _focus, _active, _focusVisible, _disabled.
|
|
41
|
+
* Smart hover bg: if `bg` is a primitive path, hover bg auto-steps one shade darker.
|
|
42
|
+
* Use `hoverScale` to specify which colour scale to step on, `hoverShade` for an
|
|
43
|
+
* exact shade, or `hoverScale={false}` to disable the smart default entirely.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* <Chamber
|
|
47
|
+
* as="button"
|
|
48
|
+
* bg="color.interactive.primary"
|
|
49
|
+
* px="space.4" py="space.2"
|
|
50
|
+
* radius="radius.md"
|
|
51
|
+
* _hover={{ bg: 'color.interactive.primary.hover', cursor: 'pointer' }}
|
|
52
|
+
* _disabled={{ opacity: 0.4, cursor: 'not-allowed' }}
|
|
53
|
+
* onClick={handleClick}
|
|
54
|
+
* />
|
|
55
|
+
*/
|
|
56
|
+
declare const Chamber: React.ForwardRefExoticComponent<ChamberProps & React.RefAttributes<HTMLElement>>;
|
|
57
|
+
|
|
58
|
+
type TunnelTag = 'span' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label';
|
|
59
|
+
interface TunnelProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, BurrowTextProps {
|
|
60
|
+
/** Element tag to render — defaults to span */
|
|
61
|
+
as?: TunnelTag;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The text component. Every piece of visible text in your UI goes in a Tunnel —
|
|
65
|
+
* headings, body copy, labels, captions, error messages.
|
|
66
|
+
*
|
|
67
|
+
* Tunnel has no layout props. Use a Chamber to position or space text alongside
|
|
68
|
+
* other elements.
|
|
69
|
+
*
|
|
70
|
+
* Priority (lowest → highest): sx → named props → style
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* <Tunnel as="h1" fontSize="fontSize.2xl" fontWeight="fontWeight.bold" color="color.text.primary">
|
|
74
|
+
* Page title
|
|
75
|
+
* </Tunnel>
|
|
76
|
+
*
|
|
77
|
+
* <Tunnel as="p" color="color.text.secondary" lineHeight="lineHeight.relaxed">
|
|
78
|
+
* Body copy here.
|
|
79
|
+
* </Tunnel>
|
|
80
|
+
*/
|
|
81
|
+
declare const Tunnel: React.ForwardRefExoticComponent<TunnelProps & React.RefAttributes<HTMLElement>>;
|
|
82
|
+
|
|
83
|
+
interface BurrowStyleRegistry {
|
|
84
|
+
/** Run a synchronous render with this registry active; returns its result. */
|
|
85
|
+
collect<T>(fn: () => T): T;
|
|
86
|
+
/** A single `<style>` tag string with every collected rule — emit in <head>. */
|
|
87
|
+
styleTags(): string;
|
|
88
|
+
/** Forget all collected rules (reuse the registry for another request). */
|
|
89
|
+
clear(): void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Server-side style collection (provider-less, framework-agnostic). Pseudo and
|
|
93
|
+
* responsive rules aren't injected on the server (there's no DOM), so collect
|
|
94
|
+
* them during `renderToString` and emit them into the document `<head>`:
|
|
95
|
+
*
|
|
96
|
+
* const registry = createStyleRegistry()
|
|
97
|
+
* const html = registry.collect(() => renderToString(<App />))
|
|
98
|
+
* // <head> … {registry.styleTags()} … </head><body>{html}</body>
|
|
99
|
+
*
|
|
100
|
+
* On the client, the emitted tag carries `data-bb-ids`; `adoptServerStyles()`
|
|
101
|
+
* (run automatically on import) seeds the cache from it so hydration never
|
|
102
|
+
* re-injects or duplicates those rules.
|
|
103
|
+
*
|
|
104
|
+
* Synchronous SSR only (`renderToString`) — the active registry is a module
|
|
105
|
+
* global set around `collect`, which is safe because Node runs sync code without
|
|
106
|
+
* interleaving. Not for streaming/async SSR.
|
|
107
|
+
*/
|
|
108
|
+
declare function createStyleRegistry(): BurrowStyleRegistry;
|
|
109
|
+
/**
|
|
110
|
+
* Seed the injection cache from a server-rendered `[data-bb-ssr]` tag so the
|
|
111
|
+
* client skips re-injecting (and thus never duplicates) SSR'd rules. Runs once
|
|
112
|
+
* automatically on import; exported for tests.
|
|
113
|
+
*/
|
|
114
|
+
declare function adoptServerStyles(): void;
|
|
115
|
+
|
|
116
|
+
export { type BurrowStyleRegistry, Chamber, type ChamberProps, Tunnel, type TunnelProps, type UseBurrowResult, adoptServerStyles, createStyleRegistry, useBurrow };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
// src/useBurrow.ts
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
import { BadgerBase } from "@purple-brock/burrow-core";
|
|
4
|
+
function useBurrow() {
|
|
5
|
+
const snapshot = useSyncExternalStore(
|
|
6
|
+
BadgerBase.subscribe,
|
|
7
|
+
BadgerBase.getSnapshot,
|
|
8
|
+
BadgerBase.getSnapshot
|
|
9
|
+
);
|
|
10
|
+
return {
|
|
11
|
+
tokens: snapshot.tokens,
|
|
12
|
+
mode: snapshot.mode,
|
|
13
|
+
setMode: BadgerBase.setMode
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/Chamber.tsx
|
|
18
|
+
import React2 from "react";
|
|
19
|
+
import {
|
|
20
|
+
resolveSx,
|
|
21
|
+
buildBoxStyles,
|
|
22
|
+
buildPseudoStyles,
|
|
23
|
+
toClassName,
|
|
24
|
+
BURROW_BOX_KEYS,
|
|
25
|
+
splitResponsiveProps,
|
|
26
|
+
getBreakpointOrder
|
|
27
|
+
} from "@purple-brock/burrow-core";
|
|
28
|
+
|
|
29
|
+
// src/styleInjector.ts
|
|
30
|
+
var ATTR = "data-bb";
|
|
31
|
+
var RESPONSIVE_ATTR = "data-bb-responsive";
|
|
32
|
+
var _injected = /* @__PURE__ */ new Set();
|
|
33
|
+
var _registry = null;
|
|
34
|
+
function createStyleRegistry() {
|
|
35
|
+
const map = /* @__PURE__ */ new Map();
|
|
36
|
+
return {
|
|
37
|
+
collect(fn) {
|
|
38
|
+
const prev = _registry;
|
|
39
|
+
_registry = map;
|
|
40
|
+
try {
|
|
41
|
+
return fn();
|
|
42
|
+
} finally {
|
|
43
|
+
_registry = prev;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
styleTags() {
|
|
47
|
+
if (map.size === 0) return "";
|
|
48
|
+
const ids = [...map.keys()].join(" ");
|
|
49
|
+
const css = [...map.values()].join("\n");
|
|
50
|
+
return `<style data-bb-ssr data-bb-ids="${ids}">${css}</style>`;
|
|
51
|
+
},
|
|
52
|
+
clear() {
|
|
53
|
+
map.clear();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function adoptServerStyles() {
|
|
58
|
+
if (typeof document === "undefined") return;
|
|
59
|
+
const ssr = document.querySelector("style[data-bb-ssr]");
|
|
60
|
+
const ids = ssr?.getAttribute("data-bb-ids");
|
|
61
|
+
if (ids) for (const id of ids.split(" ")) _injected.add(id);
|
|
62
|
+
}
|
|
63
|
+
adoptServerStyles();
|
|
64
|
+
function stableStringify(obj) {
|
|
65
|
+
const sorted = {};
|
|
66
|
+
for (const key of Object.keys(obj).sort()) {
|
|
67
|
+
const value = obj[key];
|
|
68
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
69
|
+
const inner = {};
|
|
70
|
+
for (const k of Object.keys(value).sort()) {
|
|
71
|
+
inner[k] = value[k];
|
|
72
|
+
}
|
|
73
|
+
sorted[key] = inner;
|
|
74
|
+
} else {
|
|
75
|
+
sorted[key] = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return JSON.stringify(sorted);
|
|
79
|
+
}
|
|
80
|
+
function toKebab(prop) {
|
|
81
|
+
return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
82
|
+
}
|
|
83
|
+
function styleObjectToCss(styles, important = false) {
|
|
84
|
+
const suffix = important ? " !important" : "";
|
|
85
|
+
return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => `${toKebab(k)}: ${v}${suffix};`).join(" ");
|
|
86
|
+
}
|
|
87
|
+
function pseudoSelector(pseudo) {
|
|
88
|
+
if (pseudo === "focusVisible") return "focus-visible";
|
|
89
|
+
return pseudo;
|
|
90
|
+
}
|
|
91
|
+
function injectPseudoRule(className, pseudo, styles) {
|
|
92
|
+
const id = `${className}:${pseudo}`;
|
|
93
|
+
if (_registry) {
|
|
94
|
+
if (_registry.has(id)) return;
|
|
95
|
+
const css2 = styleObjectToCss(styles, true);
|
|
96
|
+
if (css2) _registry.set(id, `.${className}:${pseudoSelector(pseudo)} { ${css2} }`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (typeof document === "undefined") return;
|
|
100
|
+
if (_injected.has(id)) return;
|
|
101
|
+
const css = styleObjectToCss(styles, true);
|
|
102
|
+
if (!css) return;
|
|
103
|
+
const rule = `.${className}:${pseudoSelector(pseudo)} { ${css} }`;
|
|
104
|
+
const existing = document.head.querySelector(`[${ATTR}="${id}"]`);
|
|
105
|
+
_injected.add(id);
|
|
106
|
+
if (existing) {
|
|
107
|
+
if (existing.textContent !== rule) existing.textContent = rule;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const tag = document.createElement("style");
|
|
111
|
+
tag.setAttribute(ATTR, id);
|
|
112
|
+
tag.textContent = rule;
|
|
113
|
+
document.head.appendChild(tag);
|
|
114
|
+
}
|
|
115
|
+
function injectPseudoStyles(className, pseudoStyles) {
|
|
116
|
+
for (const [pseudo, styles] of Object.entries(pseudoStyles)) {
|
|
117
|
+
injectPseudoRule(className, pseudo, styles);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function injectResponsiveStyles(className, bpStyles, breakpoints) {
|
|
121
|
+
const id = `resp:${className}`;
|
|
122
|
+
const rules = [];
|
|
123
|
+
const baseStyles = bpStyles.base;
|
|
124
|
+
if (baseStyles) {
|
|
125
|
+
const css = styleObjectToCss(baseStyles);
|
|
126
|
+
if (css) rules.push(`.${className} { ${css} }`);
|
|
127
|
+
}
|
|
128
|
+
const ordered = Object.entries(breakpoints).map(([bp, minWidth]) => ({ bp, minWidth, px: parseInt(minWidth, 10) })).sort((a, b) => a.px - b.px);
|
|
129
|
+
for (const { bp, minWidth } of ordered) {
|
|
130
|
+
const styles = bpStyles[bp];
|
|
131
|
+
if (!styles) continue;
|
|
132
|
+
const css = styleObjectToCss(styles);
|
|
133
|
+
if (!css) continue;
|
|
134
|
+
rules.push(`@media (min-width: ${minWidth}) { .${className} { ${css} } }`);
|
|
135
|
+
}
|
|
136
|
+
if (rules.length === 0) return;
|
|
137
|
+
const content = rules.join("\n");
|
|
138
|
+
if (_registry) {
|
|
139
|
+
if (!_registry.has(id)) _registry.set(id, content);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (typeof document === "undefined") return;
|
|
143
|
+
if (_injected.has(id)) return;
|
|
144
|
+
const existing = document.head.querySelector(`[${RESPONSIVE_ATTR}="${className}"]`);
|
|
145
|
+
_injected.add(id);
|
|
146
|
+
if (existing) {
|
|
147
|
+
if (existing.textContent !== content) existing.textContent = content;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const tag = document.createElement("style");
|
|
151
|
+
tag.setAttribute(RESPONSIVE_ATTR, className);
|
|
152
|
+
tag.textContent = content;
|
|
153
|
+
document.head.appendChild(tag);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/useDebug.ts
|
|
157
|
+
import { useRef, useLayoutEffect } from "react";
|
|
158
|
+
var DEBUG_COLOR = "#c026d3";
|
|
159
|
+
function useDebug(enabled, info) {
|
|
160
|
+
const ref = useRef(null);
|
|
161
|
+
const renders = useRef(0);
|
|
162
|
+
renders.current += 1;
|
|
163
|
+
const on = process.env.NODE_ENV !== "production" && !!enabled;
|
|
164
|
+
useLayoutEffect(() => {
|
|
165
|
+
if (!on) return;
|
|
166
|
+
const el = ref.current;
|
|
167
|
+
const r = el?.getBoundingClientRect();
|
|
168
|
+
const size = r ? `${Math.round(r.width)}\xD7${Math.round(r.height)}` : "?";
|
|
169
|
+
console.groupCollapsed(
|
|
170
|
+
`%c[bb] ${info.component} <${info.tag}> \u27F3${renders.current} \xB7 ${size} \xB7 ${info.mode}`,
|
|
171
|
+
`color:${DEBUG_COLOR};font-weight:bold`
|
|
172
|
+
);
|
|
173
|
+
console.table(info.style);
|
|
174
|
+
console.groupEnd();
|
|
175
|
+
});
|
|
176
|
+
if (!on) return { debugRef: ref };
|
|
177
|
+
return { debugRef: ref, outline: `1px dashed ${DEBUG_COLOR}`, attr: `\u27F3${renders.current}` };
|
|
178
|
+
}
|
|
179
|
+
function mergeRefs(...refs) {
|
|
180
|
+
return (node) => {
|
|
181
|
+
for (const ref of refs) {
|
|
182
|
+
if (!ref) continue;
|
|
183
|
+
if (typeof ref === "function") ref(node);
|
|
184
|
+
else ref.current = node;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/Chamber.tsx
|
|
190
|
+
import { jsx } from "react/jsx-runtime";
|
|
191
|
+
var Chamber = React2.forwardRef(function Chamber2({ as: Tag = "div", sx, style, children, __debug, ...props }, ref) {
|
|
192
|
+
const { tokens, mode } = useBurrow();
|
|
193
|
+
const burrowProps = {};
|
|
194
|
+
const htmlProps = {};
|
|
195
|
+
for (const [key, value] of Object.entries(props)) {
|
|
196
|
+
if (BURROW_BOX_KEYS.has(key)) burrowProps[key] = value;
|
|
197
|
+
else htmlProps[key] = value;
|
|
198
|
+
}
|
|
199
|
+
const { baseProps, responsiveProps } = splitResponsiveProps(burrowProps, tokens.breakpoints);
|
|
200
|
+
const boxPropsTyped = baseProps;
|
|
201
|
+
const inlineProps = {};
|
|
202
|
+
for (const key in baseProps) {
|
|
203
|
+
if (!(key in responsiveProps)) inlineProps[key] = baseProps[key];
|
|
204
|
+
}
|
|
205
|
+
const tokenStyles = buildBoxStyles(inlineProps, tokens, mode);
|
|
206
|
+
const sxStyles = sx ? resolveSx(sx, tokens, mode) : {};
|
|
207
|
+
const pseudoStyles = buildPseudoStyles(boxPropsTyped, tokens, mode);
|
|
208
|
+
const hasPseudo = Object.keys(pseudoStyles).length > 0;
|
|
209
|
+
const hasResponsive = Object.keys(responsiveProps).length > 0;
|
|
210
|
+
const breakpoints = tokens.breakpoints ?? {};
|
|
211
|
+
let bpStyles;
|
|
212
|
+
if (hasResponsive) {
|
|
213
|
+
const bpOrder = getBreakpointOrder(breakpoints);
|
|
214
|
+
bpStyles = {};
|
|
215
|
+
for (const bp of bpOrder) {
|
|
216
|
+
const bpProps = {};
|
|
217
|
+
for (const [key, obj] of Object.entries(responsiveProps)) {
|
|
218
|
+
const bpObj = obj;
|
|
219
|
+
if (bp in bpObj && bpObj[bp] !== void 0) bpProps[key] = bpObj[bp];
|
|
220
|
+
}
|
|
221
|
+
if (Object.keys(bpProps).length > 0) {
|
|
222
|
+
bpStyles[bp] = buildBoxStyles(bpProps, tokens, mode);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const className = hasPseudo || hasResponsive ? toClassName(JSON.stringify(pseudoStyles) + stableStringify(bpStyles ?? {}) + mode) : void 0;
|
|
227
|
+
if (hasPseudo && className) {
|
|
228
|
+
injectPseudoStyles(className, pseudoStyles);
|
|
229
|
+
}
|
|
230
|
+
if (hasResponsive && className && bpStyles) {
|
|
231
|
+
injectResponsiveStyles(className, bpStyles, breakpoints);
|
|
232
|
+
}
|
|
233
|
+
const resolvedStyle = { ...sxStyles, ...tokenStyles, ...style };
|
|
234
|
+
const dbg = useDebug(__debug, { component: "Chamber", tag: String(Tag), mode, style: resolvedStyle });
|
|
235
|
+
const Component = Tag;
|
|
236
|
+
const { className: userClassName, ...restHtmlProps } = htmlProps;
|
|
237
|
+
const mergedClassName = [className, userClassName].filter(Boolean).join(" ") || void 0;
|
|
238
|
+
const combinedRef = React2.useMemo(() => mergeRefs(ref, dbg.debugRef), [ref, dbg.debugRef]);
|
|
239
|
+
return /* @__PURE__ */ jsx(
|
|
240
|
+
Component,
|
|
241
|
+
{
|
|
242
|
+
ref: combinedRef,
|
|
243
|
+
className: mergedClassName,
|
|
244
|
+
style: dbg.outline ? { ...resolvedStyle, outline: dbg.outline } : resolvedStyle,
|
|
245
|
+
...dbg.attr ? { "data-bb-debug": dbg.attr } : {},
|
|
246
|
+
...restHtmlProps,
|
|
247
|
+
children
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// src/Tunnel.tsx
|
|
253
|
+
import React3 from "react";
|
|
254
|
+
import {
|
|
255
|
+
resolveSx as resolveSx2,
|
|
256
|
+
buildTextStyles,
|
|
257
|
+
buildTextPseudoStyles,
|
|
258
|
+
toClassName as toClassName2,
|
|
259
|
+
BURROW_TEXT_KEYS,
|
|
260
|
+
splitResponsiveProps as splitResponsiveProps2,
|
|
261
|
+
getBreakpointOrder as getBreakpointOrder2
|
|
262
|
+
} from "@purple-brock/burrow-core";
|
|
263
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
264
|
+
var Tunnel = React3.forwardRef(function Tunnel2({ as: Tag = "span", sx, style, children, __debug, ...props }, ref) {
|
|
265
|
+
const { tokens, mode } = useBurrow();
|
|
266
|
+
const burrowProps = {};
|
|
267
|
+
const htmlProps = {};
|
|
268
|
+
for (const [key, value] of Object.entries(props)) {
|
|
269
|
+
if (BURROW_TEXT_KEYS.has(key)) burrowProps[key] = value;
|
|
270
|
+
else htmlProps[key] = value;
|
|
271
|
+
}
|
|
272
|
+
const { baseProps, responsiveProps } = splitResponsiveProps2(burrowProps, tokens.breakpoints);
|
|
273
|
+
const textPropsTyped = baseProps;
|
|
274
|
+
const inlineProps = {};
|
|
275
|
+
for (const key in baseProps) {
|
|
276
|
+
if (!(key in responsiveProps)) inlineProps[key] = baseProps[key];
|
|
277
|
+
}
|
|
278
|
+
const tokenStyles = buildTextStyles(inlineProps, tokens, mode);
|
|
279
|
+
const sxStyles = sx ? resolveSx2(sx, tokens, mode) : {};
|
|
280
|
+
const pseudoStyles = buildTextPseudoStyles(textPropsTyped, tokens, mode);
|
|
281
|
+
const hasPseudo = Object.keys(pseudoStyles).length > 0;
|
|
282
|
+
const hasResponsive = Object.keys(responsiveProps).length > 0;
|
|
283
|
+
const breakpoints = tokens.breakpoints ?? {};
|
|
284
|
+
let bpStyles;
|
|
285
|
+
if (hasResponsive) {
|
|
286
|
+
const bpOrder = getBreakpointOrder2(breakpoints);
|
|
287
|
+
bpStyles = {};
|
|
288
|
+
for (const bp of bpOrder) {
|
|
289
|
+
const bpProps = {};
|
|
290
|
+
for (const [key, obj] of Object.entries(responsiveProps)) {
|
|
291
|
+
const bpObj = obj;
|
|
292
|
+
if (bp in bpObj && bpObj[bp] !== void 0) bpProps[key] = bpObj[bp];
|
|
293
|
+
}
|
|
294
|
+
if (Object.keys(bpProps).length > 0) {
|
|
295
|
+
bpStyles[bp] = buildTextStyles(bpProps, tokens, mode);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const className = hasPseudo || hasResponsive ? toClassName2(JSON.stringify(pseudoStyles) + stableStringify(bpStyles ?? {}) + mode) : void 0;
|
|
300
|
+
if (hasPseudo && className) {
|
|
301
|
+
injectPseudoStyles(className, pseudoStyles);
|
|
302
|
+
}
|
|
303
|
+
if (hasResponsive && className && bpStyles) {
|
|
304
|
+
injectResponsiveStyles(className, bpStyles, breakpoints);
|
|
305
|
+
}
|
|
306
|
+
const resolvedStyle = { ...sxStyles, ...tokenStyles, ...style };
|
|
307
|
+
const dbg = useDebug(__debug, { component: "Tunnel", tag: String(Tag), mode, style: resolvedStyle });
|
|
308
|
+
const Component = Tag;
|
|
309
|
+
const { className: userClassName, ...restHtmlProps } = htmlProps;
|
|
310
|
+
const mergedClassName = [className, userClassName].filter(Boolean).join(" ") || void 0;
|
|
311
|
+
const combinedRef = React3.useMemo(() => mergeRefs(ref, dbg.debugRef), [ref, dbg.debugRef]);
|
|
312
|
+
return /* @__PURE__ */ jsx2(
|
|
313
|
+
Component,
|
|
314
|
+
{
|
|
315
|
+
ref: combinedRef,
|
|
316
|
+
className: mergedClassName,
|
|
317
|
+
style: dbg.outline ? { ...resolvedStyle, outline: dbg.outline } : resolvedStyle,
|
|
318
|
+
...dbg.attr ? { "data-bb-debug": dbg.attr } : {},
|
|
319
|
+
...restHtmlProps,
|
|
320
|
+
children
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
});
|
|
324
|
+
export {
|
|
325
|
+
Chamber,
|
|
326
|
+
Tunnel,
|
|
327
|
+
adoptServerStyles,
|
|
328
|
+
createStyleRegistry,
|
|
329
|
+
useBurrow
|
|
330
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purple-brock/burrow-web",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "./dist/index.mjs",
|
|
5
|
+
"types": "./dist/index.d.mts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"default": "./dist/index.mjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@purple-brock/burrow-core": "0.1.0",
|
|
15
|
+
"@purple-brock/burrow-tokens": "0.1.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@testing-library/react": "^14.3.1",
|
|
19
|
+
"@types/jest": "^29.5.14",
|
|
20
|
+
"@types/react": "^18.3.31",
|
|
21
|
+
"jest": "^29.7.0",
|
|
22
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
23
|
+
"react": "^18.3.1",
|
|
24
|
+
"react-dom": "^18.3.1",
|
|
25
|
+
"ts-jest": "^29.4.15",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"LICENSE",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"sideEffects": false,
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/lrprice79/badger-burrow.git",
|
|
45
|
+
"directory": "packages/web"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
49
|
+
"test": "jest --passWithNoTests",
|
|
50
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
51
|
+
}
|
|
6
52
|
}
|