brand-shell 0.3.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/dist/web.d.mts ADDED
@@ -0,0 +1,61 @@
1
+ import { i as BrandTheme, n as BrandDetails, r as BrandNavLink, t as BrandAction } from "./types-PQziYg7Z.mjs";
2
+
3
+ //#region src/web/index.d.ts
4
+ declare const HTMLElementBase: typeof HTMLElement;
5
+ interface RegisterBrandShellElementsOptions {
6
+ headerTagName?: string;
7
+ footerTagName?: string;
8
+ }
9
+ interface BrandShellElementProps {
10
+ details: BrandDetails;
11
+ theme?: BrandTheme | null;
12
+ shellClass?: string | null;
13
+ }
14
+ type BrandShellElementLike = HTMLElement & {
15
+ details?: BrandDetails | null;
16
+ theme?: BrandTheme | null;
17
+ shellClass?: string | null;
18
+ };
19
+ interface SerializedBrandShellAttributes {
20
+ details: string;
21
+ theme?: string;
22
+ "shell-class"?: string;
23
+ }
24
+ declare abstract class BaseBrandShellElement extends HTMLElementBase {
25
+ static get observedAttributes(): string[];
26
+ private _details;
27
+ private _theme;
28
+ private _shellClass;
29
+ get details(): BrandDetails | null;
30
+ set details(value: BrandDetails | null);
31
+ get theme(): BrandTheme | null;
32
+ set theme(value: BrandTheme | null);
33
+ get shellClass(): string | null;
34
+ set shellClass(value: string | null);
35
+ connectedCallback(): void;
36
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void;
37
+ protected abstract build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;
38
+ private render;
39
+ private upgradeProperty;
40
+ }
41
+ declare class BrandHeaderElement extends BaseBrandShellElement {
42
+ protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;
43
+ }
44
+ declare class BrandFooterElement extends BaseBrandShellElement {
45
+ protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;
46
+ }
47
+ declare function registerBrandShellElements(options?: RegisterBrandShellElementsOptions): {
48
+ headerTagName: string;
49
+ footerTagName: string;
50
+ };
51
+ declare function applyBrandShellProps(element: BrandShellElementLike | null | undefined, props: BrandShellElementProps): void;
52
+ declare function serializeBrandShellAttributes(props: BrandShellElementProps): SerializedBrandShellAttributes;
53
+ declare global {
54
+ interface HTMLElementTagNameMap {
55
+ "brand-header": BrandHeaderElement;
56
+ "brand-footer": BrandFooterElement;
57
+ }
58
+ } //# sourceMappingURL=index.d.ts.map
59
+ //#endregion
60
+ export { type BrandAction, type BrandDetails, BrandFooterElement, BrandHeaderElement, type BrandNavLink, BrandShellElementLike, BrandShellElementProps, type BrandTheme, RegisterBrandShellElementsOptions, SerializedBrandShellAttributes, applyBrandShellProps, registerBrandShellElements, serializeBrandShellAttributes };
61
+ //# sourceMappingURL=web.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.mts","names":[],"sources":["../src/web/index.ts"],"mappings":";;;cAwBM,eAAA,SAAwB,WAAA;AAAA,UAGb,iCAAA;EACf,aAAA;EACA,aAAA;AAAA;AAAA,UAGe,sBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;AAAA;AAAA,KAGU,qBAAA,GAAwB,WAAA;EAClC,OAAA,GAAU,YAAA;EACV,KAAA,GAAQ,UAAA;EACR,UAAA;AAAA;AAAA,UAGe,8BAAA;EACf,OAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,uBASa,qBAAA,SAA8B,eAAA;EAAA,WAChC,kBAAA,CAAA;EAAA,QAIH,QAAA;EAAA,QACA,MAAA;EAAA,QACA,WAAA;EAAA,IAEJ,OAAA,CAAA,GAAW,YAAA;EAAA,IAIX,OAAA,CAAQ,KAAA,EAAO,YAAA;EAAA,IAKf,KAAA,CAAA,GAAS,UAAA;EAAA,IAIT,KAAA,CAAM,KAAA,EAAO,UAAA;EAAA,IAKb,UAAA,CAAA;EAAA,IAIA,UAAA,CAAW,KAAA;EAKf,iBAAA,CAAA;EAcA,wBAAA,CAAyB,IAAA,UAAc,SAAA,iBAA0B,QAAA;EAAA,mBAW9C,KAAA,CAAM,OAAA,EAAS,YAAA,EAAc,KAAA,EAAO,UAAA,SAAmB,UAAA,kBAA4B,WAAA;EAAA,QAE9F,MAAA;EAAA,QA2BA,eAAA;AAAA;AAAA,cASG,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CAAM,OAAA,EAAS,YAAA,EAAc,KAAA,EAAO,UAAA,SAAmB,UAAA,kBAA4B,WAAA;AAAA;AAAA,cAKlF,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CAAM,OAAA,EAAS,YAAA,EAAc,KAAA,EAAO,UAAA,SAAmB,UAAA,kBAA4B,WAAA;AAAA;AAAA,iBAK/E,0BAAA,CAA2B,OAAA,GAAS,iCAAA;;;;iBAsBpC,oBAAA,CACd,OAAA,EAAS,qBAAA,qBACT,KAAA,EAAO,sBAAA;AAAA,iBAYO,6BAAA,CAA8B,KAAA,EAAO,sBAAA,GAAyB,8BAAA;AAAA,QAgTtE,MAAA;EAAA,UACI,qBAAA;IACR,cAAA,EAAgB,kBAAA;IAChB,cAAA,EAAgB,kBAAA;EAAA;AAAA"}
package/dist/web.mjs ADDED
@@ -0,0 +1,328 @@
1
+ import { a as normalizeBrandTheme, c as themeToCssVariables, l as shouldValidateInDev, n as assertValidBrandDetails, o as validateBrandDetails, r as assertValidBrandTheme, s as validateBrandTheme, t as BrandShellValidationError, u as buildShellViewModel } from "./validation-xdqzwr3p.mjs";
2
+
3
+ //#region src/web/index.ts
4
+ const SVG_NS = "http://www.w3.org/2000/svg";
5
+ const HTMLElementBase = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
6
+ var BaseBrandShellElement = class extends HTMLElementBase {
7
+ constructor(..._args) {
8
+ super(..._args);
9
+ this._details = null;
10
+ this._theme = null;
11
+ this._shellClass = null;
12
+ }
13
+ static get observedAttributes() {
14
+ return [
15
+ "details",
16
+ "theme",
17
+ "shell-class"
18
+ ];
19
+ }
20
+ get details() {
21
+ return this._details;
22
+ }
23
+ set details(value) {
24
+ this._details = value;
25
+ this.render();
26
+ }
27
+ get theme() {
28
+ return this._theme;
29
+ }
30
+ set theme(value) {
31
+ this._theme = value;
32
+ this.render();
33
+ }
34
+ get shellClass() {
35
+ return this._shellClass;
36
+ }
37
+ set shellClass(value) {
38
+ this._shellClass = value;
39
+ this.render();
40
+ }
41
+ connectedCallback() {
42
+ this.upgradeProperty("details");
43
+ this.upgradeProperty("theme");
44
+ this.upgradeProperty("shellClass");
45
+ const attrs = parseAttributes(this);
46
+ if (this._details == null) this._details = attrs.details;
47
+ if (this._theme == null) this._theme = attrs.theme;
48
+ if (this._shellClass == null) this._shellClass = attrs.shellClass;
49
+ if (this.style.display !== "block") this.style.display = "block";
50
+ this.render();
51
+ }
52
+ attributeChangedCallback(name, _oldValue, newValue) {
53
+ if (name === "details") this._details = parseJsonAttribute(newValue);
54
+ else if (name === "theme") this._theme = parseJsonAttribute(newValue);
55
+ else if (name === "shell-class") this._shellClass = normalizeClassName(newValue);
56
+ this.render();
57
+ }
58
+ render() {
59
+ if (this._details == null) {
60
+ this.replaceChildren();
61
+ return;
62
+ }
63
+ const detailsResult = validateBrandDetails(this._details);
64
+ if (!detailsResult.valid) {
65
+ if (shouldValidateInDev()) throw new BrandShellValidationError("brand-shell/web details", detailsResult.errors);
66
+ this.replaceChildren();
67
+ return;
68
+ }
69
+ const themeResult = validateBrandTheme(this._theme);
70
+ if (!themeResult.valid && shouldValidateInDev()) throw new BrandShellValidationError("brand-shell/web theme", themeResult.errors);
71
+ const normalizedDetails = detailsResult.normalized;
72
+ const normalizedTheme = themeResult.normalized ?? null;
73
+ const element = this.build(normalizedDetails, normalizedTheme, this._shellClass);
74
+ this.replaceChildren(element);
75
+ }
76
+ upgradeProperty(propertyName) {
77
+ if (Object.prototype.hasOwnProperty.call(this, propertyName)) {
78
+ const value = this[propertyName];
79
+ delete this[propertyName];
80
+ this[propertyName] = value;
81
+ }
82
+ }
83
+ };
84
+ var BrandHeaderElement = class extends BaseBrandShellElement {
85
+ build(details, theme, shellClass) {
86
+ return createHeader(details, theme, shellClass);
87
+ }
88
+ };
89
+ var BrandFooterElement = class extends BaseBrandShellElement {
90
+ build(details, theme, shellClass) {
91
+ return createFooter(details, theme, shellClass);
92
+ }
93
+ };
94
+ function registerBrandShellElements(options = {}) {
95
+ if (typeof customElements === "undefined") throw new Error("Custom elements are not available in this environment.");
96
+ const headerTagName = options.headerTagName ?? "brand-header";
97
+ const footerTagName = options.footerTagName ?? "brand-footer";
98
+ if (!customElements.get(headerTagName)) {
99
+ const HeaderConstructor = headerTagName === "brand-header" ? BrandHeaderElement : class extends BrandHeaderElement {};
100
+ customElements.define(headerTagName, HeaderConstructor);
101
+ }
102
+ if (!customElements.get(footerTagName)) {
103
+ const FooterConstructor = footerTagName === "brand-footer" ? BrandFooterElement : class extends BrandFooterElement {};
104
+ customElements.define(footerTagName, FooterConstructor);
105
+ }
106
+ return {
107
+ headerTagName,
108
+ footerTagName
109
+ };
110
+ }
111
+ function applyBrandShellProps(element, props) {
112
+ if (!element) return;
113
+ if (shouldValidateInDev()) {
114
+ assertValidBrandDetails(props.details, "brand-shell/web details");
115
+ assertValidBrandTheme(props.theme, "brand-shell/web theme");
116
+ }
117
+ element.details = validateBrandDetails(props.details).normalized ?? props.details;
118
+ element.theme = normalizeBrandTheme(props.theme ?? null);
119
+ element.shellClass = props.shellClass ?? null;
120
+ }
121
+ function serializeBrandShellAttributes(props) {
122
+ if (shouldValidateInDev()) {
123
+ assertValidBrandDetails(props.details, "brand-shell/web serialize details");
124
+ assertValidBrandTheme(props.theme, "brand-shell/web serialize theme");
125
+ }
126
+ const normalizedDetails = validateBrandDetails(props.details).normalized ?? props.details;
127
+ const normalizedTheme = normalizeBrandTheme(props.theme ?? null);
128
+ const attributes = { details: JSON.stringify(normalizedDetails) };
129
+ if (normalizedTheme) attributes.theme = JSON.stringify(normalizedTheme);
130
+ const shellClass = normalizeClassName(props.shellClass ?? null);
131
+ if (shellClass) attributes["shell-class"] = shellClass;
132
+ return attributes;
133
+ }
134
+ function parseAttributes(element) {
135
+ return {
136
+ details: parseJsonAttribute(element.getAttribute("details")),
137
+ theme: parseJsonAttribute(element.getAttribute("theme")),
138
+ shellClass: normalizeClassName(element.getAttribute("shell-class"))
139
+ };
140
+ }
141
+ function parseJsonAttribute(value) {
142
+ if (!value) return null;
143
+ try {
144
+ return JSON.parse(value);
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+ function normalizeClassName(value) {
150
+ if (!value) return null;
151
+ const trimmed = value.trim();
152
+ return trimmed.length > 0 ? trimmed : null;
153
+ }
154
+ function createHeader(details, theme, shellClass) {
155
+ const header = document.createElement("header");
156
+ header.className = joinClassNames("brand-shell-header", shellClass);
157
+ header.setAttribute("role", "banner");
158
+ applyThemeVariables(header, theme);
159
+ header.dataset.brandCtaLayout = resolveCtaLayout(theme);
160
+ const inner = document.createElement("div");
161
+ inner.className = "brand-shell-header__inner";
162
+ const identity = details.homeHref ? createAnchor(details.homeHref, "brand-shell-header__name", details.name) : createSpan("brand-shell-header__name", details.name);
163
+ if (details.homeHref && identity instanceof HTMLAnchorElement) identity.setAttribute("aria-label", `${details.name} home`);
164
+ inner.append(identity);
165
+ const actions = document.createElement("div");
166
+ actions.className = "brand-shell-header__actions";
167
+ const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);
168
+ if (navLinks.length > 0) actions.append(createNav(navLinks, "brand-shell-header", "Primary"));
169
+ if (ctaLinks.length > 0) actions.append(createCtas(ctaLinks, "brand-shell-header__ctas"));
170
+ if (socialLinks.length > 0) actions.append(createSocialLinks(socialLinks, "brand-shell-header__social", "brand-shell-header__social-link"));
171
+ inner.append(actions);
172
+ header.append(inner);
173
+ return header;
174
+ }
175
+ function createFooter(details, theme, shellClass) {
176
+ const footer = document.createElement("footer");
177
+ footer.className = joinClassNames("brand-shell-footer", shellClass);
178
+ footer.setAttribute("role", "contentinfo");
179
+ applyThemeVariables(footer, theme);
180
+ footer.dataset.brandCtaLayout = resolveCtaLayout(theme);
181
+ const inner = document.createElement("div");
182
+ inner.className = "brand-shell-footer__inner";
183
+ const top = document.createElement("div");
184
+ top.className = "brand-shell-footer__top";
185
+ const brand = document.createElement("div");
186
+ brand.className = "brand-shell-footer__brand";
187
+ brand.append(createParagraph("brand-shell-footer__name", details.name));
188
+ if (details.tagline) brand.append(createParagraph("brand-shell-footer__tagline", details.tagline));
189
+ top.append(brand);
190
+ const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);
191
+ if (navLinks.length > 0) top.append(createNav(navLinks, "brand-shell-footer", "Footer"));
192
+ if (ctaLinks.length > 0) top.append(createCtas(ctaLinks, "brand-shell-footer__ctas"));
193
+ if (socialLinks.length > 0) top.append(createSocialLinks(socialLinks, "brand-shell-footer__social", "brand-shell-footer__social-link"));
194
+ const copy = createParagraph("brand-shell-footer__copy", `© ${(/* @__PURE__ */ new Date()).getFullYear()} ${details.name}`);
195
+ inner.append(top, copy);
196
+ footer.append(inner);
197
+ return footer;
198
+ }
199
+ function createNav(links, blockClass, ariaLabel) {
200
+ const nav = document.createElement("nav");
201
+ nav.className = `${blockClass}__nav`;
202
+ nav.setAttribute("aria-label", ariaLabel);
203
+ const list = document.createElement("ul");
204
+ list.className = `${blockClass}__list`;
205
+ for (const link of links) {
206
+ const item = document.createElement("li");
207
+ const anchor = createAnchor(link.href, `${blockClass}__link`, link.label);
208
+ anchor.target = link.target;
209
+ if (link.rel) anchor.rel = link.rel;
210
+ anchor.setAttribute("aria-label", link.ariaLabel);
211
+ item.append(anchor);
212
+ list.append(item);
213
+ }
214
+ nav.append(list);
215
+ return nav;
216
+ }
217
+ function createCtas(actions, containerClass) {
218
+ const container = document.createElement("div");
219
+ container.className = containerClass;
220
+ actions.forEach((action) => {
221
+ const anchor = createAnchor(action.href, "brand-shell-button", action.label);
222
+ anchor.className = joinClassNames(anchor.className, `brand-shell-button--${action.variant}`);
223
+ anchor.setAttribute("aria-label", action.ariaLabel);
224
+ anchor.target = action.target;
225
+ if (action.rel) anchor.rel = action.rel;
226
+ container.append(anchor);
227
+ });
228
+ return container;
229
+ }
230
+ function createSocialLinks(links, containerClass, linkClass) {
231
+ const container = document.createElement("div");
232
+ container.className = containerClass;
233
+ container.setAttribute("aria-label", "Social links");
234
+ for (const link of links) {
235
+ const anchor = createAnchor(link.href, linkClass, "");
236
+ anchor.setAttribute("aria-label", link.label);
237
+ anchor.target = "_blank";
238
+ anchor.rel = "noopener noreferrer";
239
+ const icon = createSocialIcon(link.platform);
240
+ if (icon) anchor.append(icon);
241
+ else anchor.append(createSpan("", link.label[0] ?? "?"));
242
+ container.append(anchor);
243
+ }
244
+ return container;
245
+ }
246
+ function createSocialIcon(platform) {
247
+ switch (platform) {
248
+ case "github": return createFilledIcon("M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z");
249
+ case "twitter": return createFilledIcon("M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z");
250
+ case "linkedin": return createFilledIcon("M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z");
251
+ case "discord": return createFilledIcon("M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z");
252
+ case "email": return createStrokedIcon(["m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"], { rx: "2" });
253
+ case "website": return createStrokedIcon(["M7.9 20A9 9 0 1 0 4 16.1L2 22Z"]);
254
+ default: return null;
255
+ }
256
+ }
257
+ function createFilledIcon(pathData) {
258
+ const svg = createBaseSvg();
259
+ svg.setAttribute("fill", "currentColor");
260
+ const path = document.createElementNS(SVG_NS, "path");
261
+ path.setAttribute("d", pathData);
262
+ svg.append(path);
263
+ return svg;
264
+ }
265
+ function createStrokedIcon(pathData, rectAttributes) {
266
+ const svg = createBaseSvg();
267
+ svg.setAttribute("fill", "none");
268
+ svg.setAttribute("stroke", "currentColor");
269
+ svg.setAttribute("stroke-width", "2");
270
+ svg.setAttribute("stroke-linecap", "round");
271
+ svg.setAttribute("stroke-linejoin", "round");
272
+ if (rectAttributes) {
273
+ const rect = document.createElementNS(SVG_NS, "rect");
274
+ rect.setAttribute("width", "20");
275
+ rect.setAttribute("height", "16");
276
+ rect.setAttribute("x", "2");
277
+ rect.setAttribute("y", "4");
278
+ for (const [attribute, value] of Object.entries(rectAttributes)) rect.setAttribute(attribute, value);
279
+ svg.append(rect);
280
+ }
281
+ for (const d of pathData) {
282
+ const path = document.createElementNS(SVG_NS, "path");
283
+ path.setAttribute("d", d);
284
+ svg.append(path);
285
+ }
286
+ return svg;
287
+ }
288
+ function createBaseSvg() {
289
+ const svg = document.createElementNS(SVG_NS, "svg");
290
+ svg.setAttribute("viewBox", "0 0 24 24");
291
+ svg.setAttribute("width", "1em");
292
+ svg.setAttribute("height", "1em");
293
+ svg.setAttribute("aria-hidden", "true");
294
+ return svg;
295
+ }
296
+ function applyThemeVariables(element, theme) {
297
+ const style = themeToCssVariables(theme);
298
+ for (const [name, value] of Object.entries(style)) element.style.setProperty(name, value);
299
+ }
300
+ function resolveCtaLayout(theme) {
301
+ return theme?.ctaLayout === "stacked" ? "stacked" : "inline";
302
+ }
303
+ function joinClassNames(...classNames) {
304
+ return classNames.filter(Boolean).join(" ");
305
+ }
306
+ function createAnchor(href, className, text) {
307
+ const anchor = document.createElement("a");
308
+ anchor.href = href;
309
+ anchor.className = className;
310
+ anchor.textContent = text;
311
+ return anchor;
312
+ }
313
+ function createParagraph(className, text) {
314
+ const paragraph = document.createElement("p");
315
+ paragraph.className = className;
316
+ paragraph.textContent = text;
317
+ return paragraph;
318
+ }
319
+ function createSpan(className, text) {
320
+ const span = document.createElement("span");
321
+ span.className = className;
322
+ span.textContent = text;
323
+ return span;
324
+ }
325
+
326
+ //#endregion
327
+ export { BrandFooterElement, BrandHeaderElement, applyBrandShellProps, registerBrandShellElements, serializeBrandShellAttributes };
328
+ //# sourceMappingURL=web.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.mjs","names":[],"sources":["../src/web/index.ts"],"sourcesContent":["import { buildShellViewModel, themeToCssVariables } from \"../core\";\nimport type {\n BrandDetails,\n BrandTheme,\n NormalizedBrandDetails,\n ShellActionLink,\n ShellNavLink,\n SocialLink,\n SocialPlatform,\n} from \"../core\";\nimport {\n BrandShellValidationError,\n assertValidBrandDetails,\n assertValidBrandTheme,\n normalizeBrandTheme,\n shouldValidateInDev,\n validateBrandDetails,\n validateBrandTheme,\n} from \"../core\";\n\nexport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme } from \"../core\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\nconst HTMLElementBase: typeof HTMLElement =\n typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport interface RegisterBrandShellElementsOptions {\n headerTagName?: string;\n footerTagName?: string;\n}\n\nexport interface BrandShellElementProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n}\n\nexport type BrandShellElementLike = HTMLElement & {\n details?: BrandDetails | null;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n};\n\nexport interface SerializedBrandShellAttributes {\n details: string;\n theme?: string;\n \"shell-class\"?: string;\n}\n\ninterface ParsedAttributes {\n details: BrandDetails | null;\n theme: BrandTheme | null;\n shellClass: string | null;\n}\n\nabstract class BaseBrandShellElement extends HTMLElementBase {\n static get observedAttributes() {\n return [\"details\", \"theme\", \"shell-class\"];\n }\n\n private _details: BrandDetails | null = null;\n private _theme: BrandTheme | null = null;\n private _shellClass: string | null = null;\n\n get details(): BrandDetails | null {\n return this._details;\n }\n\n set details(value: BrandDetails | null) {\n this._details = value;\n this.render();\n }\n\n get theme(): BrandTheme | null {\n return this._theme;\n }\n\n set theme(value: BrandTheme | null) {\n this._theme = value;\n this.render();\n }\n\n get shellClass(): string | null {\n return this._shellClass;\n }\n\n set shellClass(value: string | null) {\n this._shellClass = value;\n this.render();\n }\n\n connectedCallback() {\n this.upgradeProperty(\"details\");\n this.upgradeProperty(\"theme\");\n this.upgradeProperty(\"shellClass\");\n\n const attrs = parseAttributes(this);\n if (this._details == null) this._details = attrs.details;\n if (this._theme == null) this._theme = attrs.theme;\n if (this._shellClass == null) this._shellClass = attrs.shellClass;\n\n if (this.style.display !== \"block\") this.style.display = \"block\";\n this.render();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {\n if (name === \"details\") {\n this._details = parseJsonAttribute<BrandDetails>(newValue);\n } else if (name === \"theme\") {\n this._theme = parseJsonAttribute<BrandTheme>(newValue);\n } else if (name === \"shell-class\") {\n this._shellClass = normalizeClassName(newValue);\n }\n this.render();\n }\n\n protected abstract build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;\n\n private render() {\n if (this._details == null) {\n this.replaceChildren();\n return;\n }\n\n const detailsResult = validateBrandDetails(this._details);\n if (!detailsResult.valid) {\n if (shouldValidateInDev()) {\n throw new BrandShellValidationError(\"brand-shell/web details\", detailsResult.errors);\n }\n this.replaceChildren();\n return;\n }\n\n const themeResult = validateBrandTheme(this._theme);\n if (!themeResult.valid && shouldValidateInDev()) {\n throw new BrandShellValidationError(\"brand-shell/web theme\", themeResult.errors);\n }\n\n const normalizedDetails = detailsResult.normalized as NormalizedBrandDetails;\n const normalizedTheme = themeResult.normalized ?? null;\n\n const element = this.build(normalizedDetails, normalizedTheme, this._shellClass);\n this.replaceChildren(element);\n }\n\n private upgradeProperty(propertyName: \"details\" | \"theme\" | \"shellClass\") {\n if (Object.prototype.hasOwnProperty.call(this, propertyName)) {\n const value = (this as unknown as Record<string, unknown>)[propertyName];\n delete (this as unknown as Record<string, unknown>)[propertyName];\n (this as unknown as Record<string, unknown>)[propertyName] = value;\n }\n }\n}\n\nexport class BrandHeaderElement extends BaseBrandShellElement {\n protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement {\n return createHeader(details, theme, shellClass);\n }\n}\n\nexport class BrandFooterElement extends BaseBrandShellElement {\n protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement {\n return createFooter(details, theme, shellClass);\n }\n}\n\nexport function registerBrandShellElements(options: RegisterBrandShellElementsOptions = {}) {\n if (typeof customElements === \"undefined\") {\n throw new Error(\"Custom elements are not available in this environment.\");\n }\n\n const headerTagName = options.headerTagName ?? \"brand-header\";\n const footerTagName = options.footerTagName ?? \"brand-footer\";\n\n if (!customElements.get(headerTagName)) {\n const HeaderConstructor =\n headerTagName === \"brand-header\" ? BrandHeaderElement : class extends BrandHeaderElement {};\n customElements.define(headerTagName, HeaderConstructor);\n }\n if (!customElements.get(footerTagName)) {\n const FooterConstructor =\n footerTagName === \"brand-footer\" ? BrandFooterElement : class extends BrandFooterElement {};\n customElements.define(footerTagName, FooterConstructor);\n }\n\n return { headerTagName, footerTagName };\n}\n\nexport function applyBrandShellProps(\n element: BrandShellElementLike | null | undefined,\n props: BrandShellElementProps,\n) {\n if (!element) return;\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, \"brand-shell/web details\");\n assertValidBrandTheme(props.theme, \"brand-shell/web theme\");\n }\n element.details = validateBrandDetails(props.details).normalized ?? props.details;\n element.theme = normalizeBrandTheme(props.theme ?? null);\n element.shellClass = props.shellClass ?? null;\n}\n\nexport function serializeBrandShellAttributes(props: BrandShellElementProps): SerializedBrandShellAttributes {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, \"brand-shell/web serialize details\");\n assertValidBrandTheme(props.theme, \"brand-shell/web serialize theme\");\n }\n\n const normalizedDetails = validateBrandDetails(props.details).normalized ?? props.details;\n const normalizedTheme = normalizeBrandTheme(props.theme ?? null);\n\n const attributes: SerializedBrandShellAttributes = {\n details: JSON.stringify(normalizedDetails),\n };\n\n if (normalizedTheme) {\n attributes.theme = JSON.stringify(normalizedTheme);\n }\n\n const shellClass = normalizeClassName(props.shellClass ?? null);\n if (shellClass) {\n attributes[\"shell-class\"] = shellClass;\n }\n\n return attributes;\n}\n\nfunction parseAttributes(element: Element): ParsedAttributes {\n return {\n details: parseJsonAttribute<BrandDetails>(element.getAttribute(\"details\")),\n theme: parseJsonAttribute<BrandTheme>(element.getAttribute(\"theme\")),\n shellClass: normalizeClassName(element.getAttribute(\"shell-class\")),\n };\n}\n\nfunction parseJsonAttribute<T>(value: string | null): T | null {\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n}\n\nfunction normalizeClassName(value: string | null): string | null {\n if (!value) return null;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n}\n\nfunction createHeader(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement {\n const header = document.createElement(\"header\");\n header.className = joinClassNames(\"brand-shell-header\", shellClass);\n header.setAttribute(\"role\", \"banner\");\n applyThemeVariables(header, theme);\n header.dataset.brandCtaLayout = resolveCtaLayout(theme);\n\n const inner = document.createElement(\"div\");\n inner.className = \"brand-shell-header__inner\";\n\n const identity = details.homeHref ? createAnchor(details.homeHref, \"brand-shell-header__name\", details.name) : createSpan(\"brand-shell-header__name\", details.name);\n if (details.homeHref && identity instanceof HTMLAnchorElement) {\n identity.setAttribute(\"aria-label\", `${details.name} home`);\n }\n inner.append(identity);\n\n const actions = document.createElement(\"div\");\n actions.className = \"brand-shell-header__actions\";\n\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);\n\n if (navLinks.length > 0) {\n actions.append(createNav(navLinks, \"brand-shell-header\", \"Primary\"));\n }\n\n if (ctaLinks.length > 0) {\n actions.append(createCtas(ctaLinks, \"brand-shell-header__ctas\"));\n }\n\n if (socialLinks.length > 0) {\n actions.append(createSocialLinks(socialLinks, \"brand-shell-header__social\", \"brand-shell-header__social-link\"));\n }\n\n inner.append(actions);\n header.append(inner);\n return header;\n}\n\nfunction createFooter(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement {\n const footer = document.createElement(\"footer\");\n footer.className = joinClassNames(\"brand-shell-footer\", shellClass);\n footer.setAttribute(\"role\", \"contentinfo\");\n applyThemeVariables(footer, theme);\n footer.dataset.brandCtaLayout = resolveCtaLayout(theme);\n\n const inner = document.createElement(\"div\");\n inner.className = \"brand-shell-footer__inner\";\n\n const top = document.createElement(\"div\");\n top.className = \"brand-shell-footer__top\";\n\n const brand = document.createElement(\"div\");\n brand.className = \"brand-shell-footer__brand\";\n brand.append(createParagraph(\"brand-shell-footer__name\", details.name));\n if (details.tagline) {\n brand.append(createParagraph(\"brand-shell-footer__tagline\", details.tagline));\n }\n top.append(brand);\n\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);\n\n if (navLinks.length > 0) {\n top.append(createNav(navLinks, \"brand-shell-footer\", \"Footer\"));\n }\n\n if (ctaLinks.length > 0) {\n top.append(createCtas(ctaLinks, \"brand-shell-footer__ctas\"));\n }\n\n if (socialLinks.length > 0) {\n top.append(createSocialLinks(socialLinks, \"brand-shell-footer__social\", \"brand-shell-footer__social-link\"));\n }\n\n const copy = createParagraph(\"brand-shell-footer__copy\", `© ${new Date().getFullYear()} ${details.name}`);\n\n inner.append(top, copy);\n footer.append(inner);\n return footer;\n}\n\nfunction createNav(links: ShellNavLink[], blockClass: \"brand-shell-header\" | \"brand-shell-footer\", ariaLabel: string): HTMLElement {\n const nav = document.createElement(\"nav\");\n nav.className = `${blockClass}__nav`;\n nav.setAttribute(\"aria-label\", ariaLabel);\n\n const list = document.createElement(\"ul\");\n list.className = `${blockClass}__list`;\n\n for (const link of links) {\n const item = document.createElement(\"li\");\n const anchor = createAnchor(link.href, `${blockClass}__link`, link.label);\n anchor.target = link.target;\n if (link.rel) anchor.rel = link.rel;\n anchor.setAttribute(\"aria-label\", link.ariaLabel);\n item.append(anchor);\n list.append(item);\n }\n\n nav.append(list);\n return nav;\n}\n\nfunction createCtas(actions: ShellActionLink[], containerClass: string): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = containerClass;\n\n actions.forEach((action) => {\n const anchor = createAnchor(action.href, \"brand-shell-button\", action.label);\n anchor.className = joinClassNames(anchor.className, `brand-shell-button--${action.variant}`);\n anchor.setAttribute(\"aria-label\", action.ariaLabel);\n anchor.target = action.target;\n if (action.rel) anchor.rel = action.rel;\n\n container.append(anchor);\n });\n\n return container;\n}\n\nfunction createSocialLinks(\n links: SocialLink[],\n containerClass: string,\n linkClass: string,\n): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = containerClass;\n container.setAttribute(\"aria-label\", \"Social links\");\n\n for (const link of links) {\n const anchor = createAnchor(link.href, linkClass, \"\");\n anchor.setAttribute(\"aria-label\", link.label);\n anchor.target = \"_blank\";\n anchor.rel = \"noopener noreferrer\";\n\n const icon = createSocialIcon(link.platform);\n if (icon) {\n anchor.append(icon);\n } else {\n anchor.append(createSpan(\"\", link.label[0] ?? \"?\"));\n }\n\n container.append(anchor);\n }\n\n return container;\n}\n\nfunction createSocialIcon(platform: SocialPlatform): SVGSVGElement | null {\n switch (platform) {\n case \"github\":\n return createFilledIcon(\n \"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\",\n );\n case \"twitter\":\n return createFilledIcon(\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\");\n case \"linkedin\":\n return createFilledIcon(\n \"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\",\n );\n case \"discord\":\n return createFilledIcon(\n \"M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z\",\n );\n case \"email\":\n return createStrokedIcon([\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"], { rx: \"2\" });\n case \"website\":\n return createStrokedIcon([\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\"]);\n default:\n return null;\n }\n}\n\nfunction createFilledIcon(pathData: string): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"currentColor\");\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", pathData);\n svg.append(path);\n return svg;\n}\n\nfunction createStrokedIcon(pathData: string[], rectAttributes?: Record<string, string>): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n\n if (rectAttributes) {\n const rect = document.createElementNS(SVG_NS, \"rect\");\n rect.setAttribute(\"width\", \"20\");\n rect.setAttribute(\"height\", \"16\");\n rect.setAttribute(\"x\", \"2\");\n rect.setAttribute(\"y\", \"4\");\n for (const [attribute, value] of Object.entries(rectAttributes)) {\n rect.setAttribute(attribute, value);\n }\n svg.append(rect);\n }\n\n for (const d of pathData) {\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.append(path);\n }\n\n return svg;\n}\n\nfunction createBaseSvg(): SVGSVGElement {\n const svg = document.createElementNS(SVG_NS, \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"width\", \"1em\");\n svg.setAttribute(\"height\", \"1em\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n return svg;\n}\n\nfunction applyThemeVariables(element: HTMLElement, theme: BrandTheme | null) {\n const style = themeToCssVariables(theme);\n for (const [name, value] of Object.entries(style)) {\n element.style.setProperty(name, value);\n }\n}\n\nfunction resolveCtaLayout(theme: BrandTheme | null): \"inline\" | \"stacked\" {\n return theme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n}\n\nfunction joinClassNames(...classNames: Array<string | null | undefined>) {\n return classNames.filter(Boolean).join(\" \");\n}\n\nfunction createAnchor(href: string, className: string, text: string): HTMLAnchorElement {\n const anchor = document.createElement(\"a\");\n anchor.href = href;\n anchor.className = className;\n anchor.textContent = text;\n return anchor;\n}\n\nfunction createParagraph(className: string, text: string): HTMLParagraphElement {\n const paragraph = document.createElement(\"p\");\n paragraph.className = className;\n paragraph.textContent = text;\n return paragraph;\n}\n\nfunction createSpan(className: string, text: string): HTMLSpanElement {\n const span = document.createElement(\"span\");\n span.className = className;\n span.textContent = text;\n return span;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"brand-header\": BrandHeaderElement;\n \"brand-footer\": BrandFooterElement;\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "brand-shell",
3
+ "version": "0.3.0",
4
+ "description": "Reusable Header and Footer components with typed details and theme. Premium default UX, works in React, Vite, Next.js.",
5
+ "type": "module",
6
+ "main": "./dist/index.mjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.mts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.mts"
13
+ },
14
+ "./web": {
15
+ "import": "./dist/web.mjs",
16
+ "types": "./dist/web.d.mts"
17
+ },
18
+ "./vue": {
19
+ "import": "./dist/vue.mjs",
20
+ "types": "./dist/vue.d.mts"
21
+ },
22
+ "./svelte": {
23
+ "import": "./dist/svelte.mjs",
24
+ "types": "./dist/svelte.d.mts"
25
+ },
26
+ "./schema": "./dist/brand-shell.schema.json",
27
+ "./schema.json": "./dist/brand-shell.schema.json",
28
+ "./default.css": "./dist/default.css",
29
+ "./dist/default.css": "./dist/default.css"
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "prepare": "bun run build",
36
+ "build": "tsdown",
37
+ "dev": "tsdown --watch",
38
+ "demo:setup:react": "bun install --cwd examples/react-vite",
39
+ "demo:setup:vue": "bun install --cwd examples/vue-vite",
40
+ "demo:setup:svelte": "bun install --cwd examples/svelte-vite",
41
+ "demo:setup:next": "bun install --cwd examples/next-app",
42
+ "demo:setup:tanstack": "bun install --cwd examples/tanstack-vite",
43
+ "demo:setup": "bun run build && bun run demo:setup:react && bun run demo:setup:vue && bun run demo:setup:svelte && bun run demo:setup:next && bun run demo:setup:tanstack",
44
+ "demo:build:react": "bun run --cwd examples/react-vite build",
45
+ "demo:build:vue": "bun run --cwd examples/vue-vite build",
46
+ "demo:build:svelte": "bun run --cwd examples/svelte-vite build",
47
+ "demo:build:next": "bun run --cwd examples/next-app build",
48
+ "demo:build:tanstack": "bun run --cwd examples/tanstack-vite build",
49
+ "demo:build:all": "bun run build && bun run demo:build:react && bun run demo:build:vue && bun run demo:build:svelte && bun run demo:build:next && bun run demo:build:tanstack",
50
+ "demo:dev:react": "bun run build && bun run --cwd examples/react-vite dev",
51
+ "demo:dev:vue": "bun run build && bun run --cwd examples/vue-vite dev",
52
+ "demo:dev:svelte": "bun run build && bun run --cwd examples/svelte-vite dev",
53
+ "demo:dev:next": "bun run build && bun run --cwd examples/next-app dev",
54
+ "demo:dev:tanstack": "bun run build && bun run --cwd examples/tanstack-vite dev",
55
+ "docs:setup": "bun install --cwd apps/docs",
56
+ "docs:dev": "bun run build && bun run --cwd apps/docs dev",
57
+ "docs:build": "bun run build && bun run --cwd apps/docs build",
58
+ "test": "vitest run",
59
+ "test:smoke": "vitest run --config vitest.smoke.config.ts",
60
+ "typecheck": "tsc -p tsconfig.check.json --noEmit",
61
+ "lint": "bun run typecheck",
62
+ "commitlint": "commitlint --config .commitlintrc.cjs --verbose",
63
+ "pack:check": "node scripts/check-pack.cjs",
64
+ "check": "bun run lint && bun run test && bun run build && bun run pack:check",
65
+ "changeset": "bunx @changesets/cli@2.29.7",
66
+ "version:packages": "bun run changeset version",
67
+ "release:publish": "bun run changeset publish --provenance",
68
+ "storybook": "storybook dev -p 6006",
69
+ "build-storybook": "storybook build",
70
+ "postbuild": "node scripts/copy-css.cjs"
71
+ },
72
+ "peerDependencies": {
73
+ "react": ">=18.0.0",
74
+ "svelte": ">=5.0.0",
75
+ "vue": ">=3.5.0"
76
+ },
77
+ "peerDependenciesMeta": {
78
+ "react": {
79
+ "optional": true
80
+ },
81
+ "svelte": {
82
+ "optional": true
83
+ },
84
+ "vue": {
85
+ "optional": true
86
+ }
87
+ },
88
+ "devDependencies": {
89
+ "@commitlint/cli": "^19.8.1",
90
+ "@commitlint/config-conventional": "^19.8.1",
91
+ "ajv": "^8.17.1",
92
+ "@types/node": "^22.10.7",
93
+ "@types/react": "^18.3.12",
94
+ "react": "^18.3.1",
95
+ "react-dom": "^18.3.1",
96
+ "svelte": "^5.0.0",
97
+ "tsdown": "^0.20.0",
98
+ "typescript": "^5.6.3",
99
+ "vue": "^3.5.0",
100
+ "storybook": "^10.2.8",
101
+ "@storybook/react-vite": "^10.2.8",
102
+ "@chromatic-com/storybook": "^5.0.1",
103
+ "@storybook/addon-vitest": "^10.2.8",
104
+ "@storybook/addon-a11y": "^10.2.8",
105
+ "@storybook/addon-docs": "^10.2.8",
106
+ "vitest": "^4.0.18",
107
+ "playwright": "^1.58.2",
108
+ "@vitest/browser-playwright": "^4.0.18",
109
+ "@vitest/coverage-v8": "^4.0.18"
110
+ },
111
+ "license": "MIT"
112
+ }