brand-shell 0.10.0 → 0.12.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.
@@ -11,14 +11,11 @@ function detailsToSocialLinks(details) {
11
11
  href: details.linkedin,
12
12
  label: "LinkedIn"
13
13
  });
14
- if (details.gmail) {
15
- const mailHref = details.gmail.startsWith("mailto:") ? details.gmail : `mailto:${details.gmail}`;
16
- links.push({
17
- platform: "email",
18
- href: mailHref,
19
- label: "Email"
20
- });
21
- }
14
+ if (details.email) links.push({
15
+ platform: "email",
16
+ href: details.email,
17
+ label: "Email"
18
+ });
22
19
  if (details.github) links.push({
23
20
  platform: "github",
24
21
  href: details.github,
@@ -34,6 +31,12 @@ function detailsToSocialLinks(details) {
34
31
  href: details.discord,
35
32
  label: "Discord"
36
33
  });
34
+ if (details.customSocialLinks) for (const custom of details.customSocialLinks) links.push({
35
+ platform: custom.platform,
36
+ href: custom.href,
37
+ label: custom.label,
38
+ iconSvg: custom.iconSvg
39
+ });
37
40
  return links;
38
41
  }
39
42
 
@@ -91,7 +94,7 @@ function normalizeBrandDetails(details) {
91
94
  return {
92
95
  ...details,
93
96
  homeHref: normalizeSafeHref(details.homeHref),
94
- gmail: normalizeGmailHref(details.gmail),
97
+ email: normalizeEmailHref(details.email),
95
98
  website: normalizeSafeHref(details.website),
96
99
  linkedin: normalizeSafeHref(details.linkedin),
97
100
  github: normalizeSafeHref(details.github),
@@ -99,7 +102,8 @@ function normalizeBrandDetails(details) {
99
102
  discord: normalizeSafeHref(details.discord),
100
103
  navLinks: normalizeNavLinks(details.navLinks),
101
104
  primaryAction: normalizedPrimaryAction,
102
- secondaryAction: normalizedSecondaryAction
105
+ secondaryAction: normalizedSecondaryAction,
106
+ customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks)
103
107
  };
104
108
  }
105
109
  function normalizeCtaLinks(primaryAction, secondaryAction) {
@@ -113,17 +117,19 @@ function normalizeCtaLinks(primaryAction, secondaryAction) {
113
117
  };
114
118
  });
115
119
  }
116
- function buildShellViewModel(details) {
117
- const normalizedDetails = normalizeBrandDetails(details);
120
+ function buildShellViewModelFromNormalized(normalized) {
118
121
  return {
119
- navLinks: normalizedDetails.navLinks,
120
- ctaLinks: normalizeCtaLinks(normalizedDetails.primaryAction, normalizedDetails.secondaryAction),
121
- socialLinks: detailsToSocialLinks(normalizedDetails)
122
+ navLinks: normalized.navLinks,
123
+ ctaLinks: normalizeCtaLinks(normalized.primaryAction, normalized.secondaryAction),
124
+ socialLinks: detailsToSocialLinks(normalized)
122
125
  };
123
126
  }
124
- function normalizeGmailHref(gmail) {
125
- if (typeof gmail !== "string") return void 0;
126
- const trimmed = gmail.trim();
127
+ function buildShellViewModel(details) {
128
+ return buildShellViewModelFromNormalized(normalizeBrandDetails(details));
129
+ }
130
+ function normalizeEmailHref(email) {
131
+ if (typeof email !== "string") return void 0;
132
+ const trimmed = email.trim();
127
133
  if (trimmed.length === 0) return void 0;
128
134
  if (trimmed.toLowerCase().startsWith("mailto:")) {
129
135
  const address = trimmed.slice(7).trim();
@@ -132,6 +138,19 @@ function normalizeGmailHref(gmail) {
132
138
  }
133
139
  return normalizeSafeHref(`mailto:${trimmed}`);
134
140
  }
141
+ function normalizeCustomSocialLinks(links) {
142
+ if (!links || links.length === 0) return void 0;
143
+ const normalized = [];
144
+ for (const link of links) {
145
+ const href = normalizeSafeHref(link.href);
146
+ if (!href) continue;
147
+ normalized.push({
148
+ ...link,
149
+ href
150
+ });
151
+ }
152
+ return normalized.length > 0 ? normalized : void 0;
153
+ }
135
154
  function normalizeAction(action) {
136
155
  if (!action) return void 0;
137
156
  const href = normalizeSafeHref(action.href);
@@ -182,10 +201,14 @@ function themeToCssVariables(theme) {
182
201
  const buttonTextColor = getAccessibleTextColor(theme.primaryColor);
183
202
  if (buttonTextColor) style[`--${THEME_VAR_PREFIX}-button-text`] = buttonTextColor;
184
203
  }
204
+ if (theme.borderRadius != null) style[`--${THEME_VAR_PREFIX}-radius`] = theme.borderRadius;
205
+ if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;
206
+ if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;
207
+ if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;
185
208
  return style;
186
209
  }
187
210
  function getAccessibleTextColor(backgroundColor) {
188
- const rgb = parseHexColor(backgroundColor);
211
+ const rgb = parseColorToRgb(backgroundColor);
189
212
  if (!rgb) return void 0;
190
213
  return contrastRatio(rgb, {
191
214
  r: 15,
@@ -197,20 +220,31 @@ function getAccessibleTextColor(backgroundColor) {
197
220
  b: 252
198
221
  }) ? DARK_TEXT : LIGHT_TEXT;
199
222
  }
200
- function parseHexColor(color) {
223
+ /**
224
+ * Parses a hex (#rgb / #rrggbb) or rgb()/rgba() color string to RGB components.
225
+ * hsl(), oklch(), and other formats are not supported and return undefined.
226
+ */
227
+ function parseColorToRgb(color) {
201
228
  const trimmed = color.trim();
202
- const match = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);
203
- if (!match) return void 0;
204
- const value = match[1];
205
- if (value.length === 3) return {
206
- r: Number.parseInt(value[0] + value[0], 16),
207
- g: Number.parseInt(value[1] + value[1], 16),
208
- b: Number.parseInt(value[2] + value[2], 16)
209
- };
210
- return {
211
- r: Number.parseInt(value.slice(0, 2), 16),
212
- g: Number.parseInt(value.slice(2, 4), 16),
213
- b: Number.parseInt(value.slice(4, 6), 16)
229
+ const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);
230
+ if (hexMatch) {
231
+ const value = hexMatch[1];
232
+ if (value.length === 3) return {
233
+ r: Number.parseInt(value[0] + value[0], 16),
234
+ g: Number.parseInt(value[1] + value[1], 16),
235
+ b: Number.parseInt(value[2] + value[2], 16)
236
+ };
237
+ return {
238
+ r: Number.parseInt(value.slice(0, 2), 16),
239
+ g: Number.parseInt(value.slice(2, 4), 16),
240
+ b: Number.parseInt(value.slice(4, 6), 16)
241
+ };
242
+ }
243
+ const rgbMatch = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(trimmed);
244
+ if (rgbMatch) return {
245
+ r: +rgbMatch[1],
246
+ g: +rgbMatch[2],
247
+ b: +rgbMatch[3]
214
248
  };
215
249
  }
216
250
  function contrastRatio(first, second) {
@@ -254,7 +288,11 @@ const THEME_KEYS = new Set([
254
288
  "linkColor",
255
289
  "socialIconSize",
256
290
  "buttonTextColor",
257
- "ctaLayout"
291
+ "ctaLayout",
292
+ "borderRadius",
293
+ "headerHeight",
294
+ "footerPadding",
295
+ "secondaryButtonBg"
258
296
  ]);
259
297
  var BrandShellValidationError = class extends Error {
260
298
  constructor(context, errors) {
@@ -281,8 +319,8 @@ function validateBrandDetails(details) {
281
319
  validateSafeHref(details.website, "details.website", errors);
282
320
  validateOptionalString(details.linkedin, "details.linkedin", errors);
283
321
  validateSafeHref(details.linkedin, "details.linkedin", errors);
284
- validateOptionalString(details.gmail, "details.gmail", errors);
285
- validateGmail(details.gmail, "details.gmail", errors);
322
+ validateOptionalString(details.email, "details.email", errors);
323
+ validateEmail(details.email, "details.email", errors);
286
324
  validateOptionalString(details.github, "details.github", errors);
287
325
  validateSafeHref(details.github, "details.github", errors);
288
326
  validateOptionalString(details.twitter, "details.twitter", errors);
@@ -294,6 +332,8 @@ function validateBrandDetails(details) {
294
332
  else details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));
295
333
  if (details.primaryAction != null) validateAction(details.primaryAction, "details.primaryAction", errors);
296
334
  if (details.secondaryAction != null) validateAction(details.secondaryAction, "details.secondaryAction", errors);
335
+ if (details.customSocialLinks != null) if (!Array.isArray(details.customSocialLinks)) errors.push("details.customSocialLinks must be an array.");
336
+ else details.customSocialLinks.forEach((link, index) => validateCustomSocialLink(link, `details.customSocialLinks[${index}]`, errors));
297
337
  if (errors.length > 0) return {
298
338
  valid: false,
299
339
  errors,
@@ -396,6 +436,17 @@ function validateAction(action, path, errors) {
396
436
  if (typeof action.variant !== "string" || !CTA_VARIANTS.has(action.variant)) errors.push(`${path}.variant must be one of: primary, secondary, ghost.`);
397
437
  }
398
438
  }
439
+ function validateCustomSocialLink(link, path, errors) {
440
+ if (!isRecord(link)) {
441
+ errors.push(`${path} must be an object.`);
442
+ return;
443
+ }
444
+ if (typeof link.platform !== "string" || link.platform.trim().length === 0) errors.push(`${path}.platform must be a non-empty string.`);
445
+ validateRequiredString(link.label, `${path}.label`, errors);
446
+ validateRequiredString(link.href, `${path}.href`, errors);
447
+ validateSafeHref(link.href, `${path}.href`, errors);
448
+ if (link.iconSvg != null) validateOptionalString(link.iconSvg, `${path}.iconSvg`, errors);
449
+ }
399
450
  function validateTarget(target, path, errors) {
400
451
  if (target == null) return;
401
452
  if (typeof target !== "string" || !LINK_TARGETS.has(target)) errors.push(`${path} must be one of: _blank, _self, _parent, _top.`);
@@ -416,15 +467,15 @@ function validateSafeHref(value, path, errors) {
416
467
  if (typeof value !== "string" || value.trim().length === 0) return;
417
468
  if (!normalizeSafeHref(value)) errors.push(`${path} must use a safe URL/path (http, https, mailto, tel, or relative path).`);
418
469
  }
419
- function validateGmail(value, path, errors) {
470
+ function validateEmail(value, path, errors) {
420
471
  if (value == null) return;
421
472
  if (typeof value !== "string" || value.trim().length === 0) return;
422
- if (!normalizeGmailHref(value)) errors.push(`${path} must be a valid email or mailto URL.`);
473
+ if (!normalizeEmailHref(value)) errors.push(`${path} must be a valid email or mailto URL.`);
423
474
  }
424
475
  function isRecord(value) {
425
476
  return typeof value === "object" && value !== null;
426
477
  }
427
478
 
428
479
  //#endregion
429
- export { normalizeBrandTheme as a, themeToCssVariables as c, normalizeBrandDetails as d, normalizeCtaLinks as f, detailsToSocialLinks as h, formatValidationErrors as i, shouldValidateInDev as l, normalizeNavLinks as m, assertValidBrandDetails as n, validateBrandDetails as o, normalizeGmailHref as p, assertValidBrandTheme as r, validateBrandTheme as s, BrandShellValidationError as t, buildShellViewModel as u };
430
- //# sourceMappingURL=validation-xdqzwr3p.mjs.map
480
+ export { normalizeBrandTheme as a, themeToCssVariables as c, buildShellViewModelFromNormalized as d, normalizeBrandDetails as f, detailsToSocialLinks as g, normalizeNavLinks as h, formatValidationErrors as i, shouldValidateInDev as l, normalizeEmailHref as m, assertValidBrandDetails as n, validateBrandDetails as o, normalizeCtaLinks as p, assertValidBrandTheme as r, validateBrandTheme as s, BrandShellValidationError as t, buildShellViewModel as u };
481
+ //# sourceMappingURL=validation-CtH2UkVv.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-CtH2UkVv.mjs","names":[],"sources":["../src/core/social.ts","../src/core/links.ts","../src/core/shell.ts","../src/core/dev.ts","../src/core/theme.ts","../src/core/validation.ts"],"sourcesContent":["import type { BrandDetails } from \"./types\";\n\nexport type SocialPlatform = \"website\" | \"linkedin\" | \"email\" | \"github\" | \"twitter\" | \"discord\";\n\nexport interface SocialLink {\n platform: SocialPlatform | string;\n href: string;\n label: string;\n iconSvg?: string;\n}\n\nexport function detailsToSocialLinks(details: BrandDetails): SocialLink[] {\n const links: SocialLink[] = [];\n if (details.website) links.push({ platform: \"website\", href: details.website, label: \"Website\" });\n if (details.linkedin) links.push({ platform: \"linkedin\", href: details.linkedin, label: \"LinkedIn\" });\n if (details.email) links.push({ platform: \"email\", href: details.email, label: \"Email\" });\n if (details.github) links.push({ platform: \"github\", href: details.github, label: \"GitHub\" });\n if (details.twitter) links.push({ platform: \"twitter\", href: details.twitter, label: \"Twitter\" });\n if (details.discord) links.push({ platform: \"discord\", href: details.discord, label: \"Discord\" });\n if (details.customSocialLinks) {\n for (const custom of details.customSocialLinks) {\n links.push({ platform: custom.platform, href: custom.href, label: custom.label, iconSvg: custom.iconSvg });\n }\n }\n return links;\n}\n","const ABSOLUTE_SCHEME_PATTERN = /^([a-zA-Z][a-zA-Z\\d+.-]*):/;\nconst UNSAFE_CHAR_PATTERN = /[\\u0000-\\u001f\\u007f]/;\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"]);\nconst REQUIRED_BLANK_REL_TOKENS = [\"noopener\", \"noreferrer\"];\n\nexport function normalizeSafeHref(href: unknown): string | undefined {\n if (typeof href !== \"string\") return undefined;\n const trimmed = href.trim();\n if (trimmed.length === 0) return undefined;\n if (UNSAFE_CHAR_PATTERN.test(trimmed)) return undefined;\n if (trimmed.startsWith(\"//\")) return undefined;\n\n const schemeMatch = trimmed.match(ABSOLUTE_SCHEME_PATTERN);\n if (!schemeMatch) {\n return trimmed;\n }\n\n const protocol = `${schemeMatch[1]?.toLowerCase()}:`;\n if (!ALLOWED_PROTOCOLS.has(protocol)) {\n return undefined;\n }\n\n return trimmed;\n}\n\nexport function isSafeHref(href: unknown): boolean {\n return normalizeSafeHref(href) != null;\n}\n\nexport function normalizeRel(target: \"_blank\" | \"_self\" | \"_parent\" | \"_top\", rel?: string): string | undefined {\n const normalizedRel = typeof rel === \"string\" ? rel.trim() : \"\";\n if (target !== \"_blank\") {\n return normalizedRel.length > 0 ? normalizedRel : undefined;\n }\n\n const tokens = normalizedRel.length > 0 ? normalizedRel.split(/\\s+/).filter(Boolean) : [];\n const tokenSet = new Set(tokens.map((token) => token.toLowerCase()));\n\n for (const requiredToken of REQUIRED_BLANK_REL_TOKENS) {\n if (!tokenSet.has(requiredToken)) {\n tokens.push(requiredToken);\n }\n }\n\n return tokens.join(\" \");\n}\n","import { detailsToSocialLinks, type SocialLink } from \"./social\";\nimport type { BrandAction, BrandDetails, BrandNavLink, CustomSocialLink } from \"./types\";\nimport { normalizeRel, normalizeSafeHref } from \"./links\";\n\nexport type LinkTarget = NonNullable<BrandNavLink[\"target\"]>;\n\nexport interface ShellNavLink extends BrandNavLink {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n}\n\nexport interface ShellActionLink extends BrandAction {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n variant: NonNullable<BrandAction[\"variant\"]>;\n}\n\nexport interface ShellViewModel {\n navLinks: ShellNavLink[];\n ctaLinks: ShellActionLink[];\n socialLinks: SocialLink[];\n}\n\nexport type NormalizedActionLink = Omit<ShellActionLink, \"variant\"> & Pick<BrandAction, \"variant\">;\n\nexport interface NormalizedBrandDetails extends Omit<BrandDetails, \"navLinks\" | \"primaryAction\" | \"secondaryAction\"> {\n navLinks: ShellNavLink[];\n primaryAction?: NormalizedActionLink;\n secondaryAction?: NormalizedActionLink;\n}\n\nexport function normalizeNavLinks(navLinks: BrandNavLink[] = []): ShellNavLink[] {\n return navLinks.flatMap((link) => {\n const href = normalizeSafeHref(link.href);\n if (!href) return [];\n\n const { target, rel } = normalizeLinkTargetRel(link.target, link.rel);\n return {\n ...link,\n href,\n ariaLabel: link.ariaLabel ?? link.label,\n target,\n rel,\n };\n });\n}\n\nexport function normalizeBrandDetails(details: BrandDetails): NormalizedBrandDetails {\n const normalizedPrimaryAction = normalizeAction(details.primaryAction);\n const normalizedSecondaryAction = normalizeAction(details.secondaryAction);\n\n return {\n ...details,\n homeHref: normalizeSafeHref(details.homeHref),\n email: normalizeEmailHref(details.email),\n website: normalizeSafeHref(details.website),\n linkedin: normalizeSafeHref(details.linkedin),\n github: normalizeSafeHref(details.github),\n twitter: normalizeSafeHref(details.twitter),\n discord: normalizeSafeHref(details.discord),\n navLinks: normalizeNavLinks(details.navLinks),\n primaryAction: normalizedPrimaryAction,\n secondaryAction: normalizedSecondaryAction,\n customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks),\n };\n}\n\nexport function normalizeCtaLinks(\n primaryAction?: BrandAction,\n secondaryAction?: BrandAction,\n): ShellActionLink[] {\n const normalizedPrimaryAction = normalizeAction(primaryAction);\n const normalizedSecondaryAction = normalizeAction(secondaryAction);\n const actions = [normalizedSecondaryAction, normalizedPrimaryAction].filter(\n (action): action is NonNullable<typeof action> => Boolean(action),\n );\n\n return actions.map((action, index) => {\n const variant =\n action.variant ?? (action === normalizedPrimaryAction || index === actions.length - 1 ? \"primary\" : \"secondary\");\n\n return {\n ...action,\n variant,\n };\n });\n}\n\nexport function buildShellViewModelFromNormalized(normalized: NormalizedBrandDetails): ShellViewModel {\n return {\n navLinks: normalized.navLinks,\n ctaLinks: normalizeCtaLinks(normalized.primaryAction, normalized.secondaryAction),\n socialLinks: detailsToSocialLinks(normalized),\n };\n}\n\nexport function buildShellViewModel(details: BrandDetails): ShellViewModel {\n return buildShellViewModelFromNormalized(normalizeBrandDetails(details));\n}\n\nexport function normalizeEmailHref(email?: string): string | undefined {\n if (typeof email !== \"string\") return undefined;\n const trimmed = email.trim();\n if (trimmed.length === 0) return undefined;\n if (trimmed.toLowerCase().startsWith(\"mailto:\")) {\n const address = trimmed.slice(7).trim();\n if (address.length === 0) return undefined;\n return normalizeSafeHref(`mailto:${address}`);\n }\n return normalizeSafeHref(`mailto:${trimmed}`);\n}\n\nfunction normalizeCustomSocialLinks(links?: CustomSocialLink[]): CustomSocialLink[] | undefined {\n if (!links || links.length === 0) return undefined;\n const normalized: CustomSocialLink[] = [];\n for (const link of links) {\n const href = normalizeSafeHref(link.href);\n if (!href) continue;\n normalized.push({ ...link, href });\n }\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeAction(action?: BrandAction): NormalizedActionLink | undefined {\n if (!action) return undefined;\n\n const href = normalizeSafeHref(action.href);\n if (!href) return undefined;\n\n const { target, rel } = normalizeLinkTargetRel(action.target, action.rel);\n return {\n ...action,\n href,\n target,\n rel,\n ariaLabel: action.ariaLabel ?? action.label,\n };\n}\n\nexport function normalizeLinkTargetRel(target?: BrandNavLink[\"target\"], rel?: string) {\n const normalizedTarget: LinkTarget = target ?? \"_self\";\n const normalizedRel = normalizeRel(normalizedTarget, rel);\n return {\n target: normalizedTarget,\n rel: normalizedRel,\n };\n}\n","type ImportMetaEnv = {\n DEV?: boolean;\n MODE?: string;\n};\n\ntype ImportMetaLike = {\n env?: ImportMetaEnv;\n};\n\nexport function shouldValidateInDev(): boolean {\n const nodeEnv = typeof process !== \"undefined\" ? process.env?.NODE_ENV : undefined;\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n const { env } = import.meta as unknown as ImportMetaLike;\n if (typeof env?.DEV === \"boolean\") {\n return env.DEV;\n }\n\n return false;\n}\n","import type { BrandTheme } from \"./types\";\n\nconst THEME_VAR_PREFIX = \"brand\";\nconst DARK_TEXT = \"#0f172a\";\nconst LIGHT_TEXT = \"#f8fafc\";\n\nexport type ThemeVariables = Record<string, string>;\n\nexport function themeToCssVariables(theme?: BrandTheme | null): ThemeVariables {\n if (!theme) return {};\n const style: ThemeVariables = {};\n if (theme.primaryColor != null) style[`--${THEME_VAR_PREFIX}-primary`] = theme.primaryColor;\n if (theme.backgroundColor != null) style[`--${THEME_VAR_PREFIX}-bg`] = theme.backgroundColor;\n if (theme.textColor != null) style[`--${THEME_VAR_PREFIX}-text`] = theme.textColor;\n if (theme.fontFamily != null) style[`--${THEME_VAR_PREFIX}-font`] = theme.fontFamily;\n if (theme.linkColor != null) style[`--${THEME_VAR_PREFIX}-link`] = theme.linkColor;\n if (theme.socialIconSize != null) style[`--${THEME_VAR_PREFIX}-social-size`] = theme.socialIconSize;\n if (theme.buttonTextColor != null) {\n style[`--${THEME_VAR_PREFIX}-button-text`] = theme.buttonTextColor;\n } else if (theme.primaryColor != null) {\n const buttonTextColor = getAccessibleTextColor(theme.primaryColor);\n if (buttonTextColor) style[`--${THEME_VAR_PREFIX}-button-text`] = buttonTextColor;\n }\n if (theme.borderRadius != null) style[`--${THEME_VAR_PREFIX}-radius`] = theme.borderRadius;\n if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;\n if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;\n if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;\n return style;\n}\n\nfunction getAccessibleTextColor(backgroundColor: string): string | undefined {\n const rgb = parseColorToRgb(backgroundColor);\n if (!rgb) return undefined;\n\n const contrastWithDark = contrastRatio(rgb, { r: 15, g: 23, b: 42 });\n const contrastWithLight = contrastRatio(rgb, { r: 248, g: 250, b: 252 });\n\n return contrastWithDark > contrastWithLight ? DARK_TEXT : LIGHT_TEXT;\n}\n\n/**\n * Parses a hex (#rgb / #rrggbb) or rgb()/rgba() color string to RGB components.\n * hsl(), oklch(), and other formats are not supported and return undefined.\n */\nfunction parseColorToRgb(color: string): { r: number; g: number; b: number } | undefined {\n const trimmed = color.trim();\n\n const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);\n if (hexMatch) {\n const value = hexMatch[1];\n if (value.length === 3) {\n return {\n r: Number.parseInt(value[0] + value[0], 16),\n g: Number.parseInt(value[1] + value[1], 16),\n b: Number.parseInt(value[2] + value[2], 16),\n };\n }\n return {\n r: Number.parseInt(value.slice(0, 2), 16),\n g: Number.parseInt(value.slice(2, 4), 16),\n b: Number.parseInt(value.slice(4, 6), 16),\n };\n }\n\n const rgbMatch = /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/.exec(trimmed);\n if (rgbMatch) {\n return { r: +rgbMatch[1], g: +rgbMatch[2], b: +rgbMatch[3] };\n }\n\n return undefined;\n}\n\nfunction contrastRatio(\n first: { r: number; g: number; b: number },\n second: { r: number; g: number; b: number },\n): number {\n const firstLuminance = relativeLuminance(first);\n const secondLuminance = relativeLuminance(second);\n const lighter = Math.max(firstLuminance, secondLuminance);\n const darker = Math.min(firstLuminance, secondLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction relativeLuminance(color: { r: number; g: number; b: number }): number {\n const transform = (channel: number) => {\n const normalized = channel / 255;\n if (normalized <= 0.03928) return normalized / 12.92;\n return ((normalized + 0.055) / 1.055) ** 2.4;\n };\n\n const r = transform(color.r);\n const g = transform(color.g);\n const b = transform(color.b);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n","import { normalizeBrandDetails, normalizeEmailHref, type NormalizedBrandDetails } from \"./shell\";\nimport { normalizeSafeHref } from \"./links\";\nimport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme, CustomSocialLink } from \"./types\";\n\nconst LINK_TARGETS = new Set<NonNullable<BrandNavLink[\"target\"]>>([\"_blank\", \"_self\", \"_parent\", \"_top\"]);\nconst CTA_VARIANTS = new Set<NonNullable<BrandAction[\"variant\"]>>([\"primary\", \"secondary\", \"ghost\"]);\nconst CTA_LAYOUTS = new Set<NonNullable<BrandTheme[\"ctaLayout\"]>>([\"inline\", \"stacked\"]);\nconst THEME_KEYS = new Set<keyof BrandTheme>([\n \"primaryColor\",\n \"backgroundColor\",\n \"textColor\",\n \"fontFamily\",\n \"linkColor\",\n \"socialIconSize\",\n \"buttonTextColor\",\n \"ctaLayout\",\n \"borderRadius\",\n \"headerHeight\",\n \"footerPadding\",\n \"secondaryButtonBg\",\n]);\n\ntype ValidationErrorPath = string;\n\nexport interface BrandValidationResult<T> {\n valid: boolean;\n errors: string[];\n normalized: T | null;\n}\n\nexport class BrandShellValidationError extends Error {\n readonly context: string;\n readonly errors: string[];\n\n constructor(context: string, errors: string[]) {\n super(formatValidationErrors(context, errors));\n this.name = \"BrandShellValidationError\";\n this.context = context;\n this.errors = errors;\n }\n}\n\nexport function validateBrandDetails(details: unknown): BrandValidationResult<NormalizedBrandDetails> {\n const errors: string[] = [];\n\n if (!isRecord(details)) {\n errors.push(\"details must be an object.\");\n return { valid: false, errors, normalized: null };\n }\n\n validateRequiredString(details.name, \"details.name\", errors);\n validateOptionalString(details.homeHref, \"details.homeHref\", errors);\n validateSafeHref(details.homeHref, \"details.homeHref\", errors);\n validateOptionalString(details.website, \"details.website\", errors);\n validateSafeHref(details.website, \"details.website\", errors);\n validateOptionalString(details.linkedin, \"details.linkedin\", errors);\n validateSafeHref(details.linkedin, \"details.linkedin\", errors);\n validateOptionalString(details.email, \"details.email\", errors);\n validateEmail(details.email, \"details.email\", errors);\n validateOptionalString(details.github, \"details.github\", errors);\n validateSafeHref(details.github, \"details.github\", errors);\n validateOptionalString(details.twitter, \"details.twitter\", errors);\n validateSafeHref(details.twitter, \"details.twitter\", errors);\n validateOptionalString(details.discord, \"details.discord\", errors);\n validateSafeHref(details.discord, \"details.discord\", errors);\n validateOptionalString(details.tagline, \"details.tagline\", errors);\n\n if (details.navLinks != null) {\n if (!Array.isArray(details.navLinks)) {\n errors.push(\"details.navLinks must be an array.\");\n } else {\n details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));\n }\n }\n\n if (details.primaryAction != null) {\n validateAction(details.primaryAction, \"details.primaryAction\", errors);\n }\n if (details.secondaryAction != null) {\n validateAction(details.secondaryAction, \"details.secondaryAction\", errors);\n }\n\n if (details.customSocialLinks != null) {\n if (!Array.isArray(details.customSocialLinks)) {\n errors.push(\"details.customSocialLinks must be an array.\");\n } else {\n details.customSocialLinks.forEach((link, index) =>\n validateCustomSocialLink(link, `details.customSocialLinks[${index}]`, errors),\n );\n }\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandDetails(details as unknown as BrandDetails),\n };\n}\n\nexport function validateBrandTheme(theme: unknown): BrandValidationResult<BrandTheme | null> {\n if (theme == null) {\n return {\n valid: true,\n errors: [],\n normalized: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!isRecord(theme)) {\n errors.push(\"theme must be an object when provided.\");\n return { valid: false, errors, normalized: null };\n }\n\n for (const key of Object.keys(theme)) {\n if (!THEME_KEYS.has(key as keyof BrandTheme)) {\n errors.push(`theme.${key} is not a supported theme key.`);\n continue;\n }\n if (key === \"ctaLayout\") {\n validateCtaLayout(theme[key], \"theme.ctaLayout\", errors);\n continue;\n }\n validateOptionalString(theme[key], `theme.${key}`, errors);\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandTheme(theme as BrandTheme),\n };\n}\n\nexport function assertValidBrandDetails(details: unknown, context = \"BrandDetails\"): asserts details is BrandDetails {\n const result = validateBrandDetails(details);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function assertValidBrandTheme(theme: unknown, context = \"BrandTheme\"): asserts theme is BrandTheme | null | undefined {\n const result = validateBrandTheme(theme);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | null {\n if (!theme) return null;\n\n const normalized: BrandTheme = {};\n for (const key of THEME_KEYS) {\n const value = theme[key];\n if (key === \"ctaLayout\") {\n if (typeof value === \"string\" && CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n normalized[key] = value as BrandTheme[\"ctaLayout\"];\n }\n continue;\n }\n\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length > 0) {\n normalized[key] = trimmed;\n }\n }\n }\n\n return Object.keys(normalized).length > 0 ? normalized : null;\n}\n\nexport function formatValidationErrors(context: string, errors: string[]): string {\n return `${context} validation failed:\\n${errors.map((error) => `- ${error}`).join(\"\\n\")}`;\n}\n\nfunction validateNavLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n validateOptionalString(link.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(link.rel, `${path}.rel`, errors);\n validateTarget(link.target, `${path}.target`, errors);\n}\n\nfunction validateAction(action: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(action)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(action.label, `${path}.label`, errors);\n validateRequiredString(action.href, `${path}.href`, errors);\n validateSafeHref(action.href, `${path}.href`, errors);\n validateOptionalString(action.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(action.rel, `${path}.rel`, errors);\n validateTarget(action.target, `${path}.target`, errors);\n\n if (action.variant != null) {\n if (typeof action.variant !== \"string\" || !CTA_VARIANTS.has(action.variant as NonNullable<BrandAction[\"variant\"]>)) {\n errors.push(`${path}.variant must be one of: primary, secondary, ghost.`);\n }\n }\n}\n\nfunction validateCustomSocialLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n if (typeof link.platform !== \"string\" || link.platform.trim().length === 0) {\n errors.push(`${path}.platform must be a non-empty string.`);\n }\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n if (link.iconSvg != null) {\n validateOptionalString(link.iconSvg, `${path}.iconSvg`, errors);\n }\n}\n\nfunction validateTarget(target: unknown, path: ValidationErrorPath, errors: string[]) {\n if (target == null) return;\n if (typeof target !== \"string\" || !LINK_TARGETS.has(target as NonNullable<BrandNavLink[\"target\"]>)) {\n errors.push(`${path} must be one of: _blank, _self, _parent, _top.`);\n }\n}\n\nfunction validateCtaLayout(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || !CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n errors.push(`${path} must be one of: inline, stacked.`);\n }\n}\n\nfunction validateRequiredString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string.`);\n }\n}\n\nfunction validateOptionalString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string when provided.`);\n }\n}\n\nfunction validateSafeHref(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeSafeHref(value)) {\n errors.push(`${path} must use a safe URL/path (http, https, mailto, tel, or relative path).`);\n }\n}\n\nfunction validateEmail(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeEmailHref(value)) {\n errors.push(`${path} must be a valid email or mailto URL.`);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"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"}
package/dist/vue.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { i as BrandTheme, n as BrandDetails } from "./types-PQziYg7Z.mjs";
1
+ import { i as BrandTheme, n as BrandDetails } from "./types-B_CLRZMO.mjs";
2
+ import { LinkFactoryOptions } from "./web.mjs";
2
3
  import * as vue from "vue";
3
4
  import { PropType } from "vue";
4
5
 
@@ -7,6 +8,7 @@ interface BrandShellVueProps {
7
8
  details: BrandDetails;
8
9
  theme?: BrandTheme | null;
9
10
  shellClass?: string | null;
11
+ linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;
10
12
  }
11
13
  declare const BrandHeader: vue.DefineComponent<vue.ExtractPropTypes<{
12
14
  details: {
@@ -21,6 +23,10 @@ declare const BrandHeader: vue.DefineComponent<vue.ExtractPropTypes<{
21
23
  type: PropType<string | null>;
22
24
  default: null;
23
25
  };
26
+ linkFactory: {
27
+ type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
28
+ default: null;
29
+ };
24
30
  }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
25
31
  [key: string]: any;
26
32
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -36,9 +42,14 @@ declare const BrandHeader: vue.DefineComponent<vue.ExtractPropTypes<{
36
42
  type: PropType<string | null>;
37
43
  default: null;
38
44
  };
45
+ linkFactory: {
46
+ type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
47
+ default: null;
48
+ };
39
49
  }>> & Readonly<{}>, {
40
50
  theme: BrandTheme | null;
41
51
  shellClass: string | null;
52
+ linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;
42
53
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
43
54
  declare const BrandFooter: vue.DefineComponent<vue.ExtractPropTypes<{
44
55
  details: {
@@ -53,6 +64,10 @@ declare const BrandFooter: vue.DefineComponent<vue.ExtractPropTypes<{
53
64
  type: PropType<string | null>;
54
65
  default: null;
55
66
  };
67
+ linkFactory: {
68
+ type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
69
+ default: null;
70
+ };
56
71
  }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
57
72
  [key: string]: any;
58
73
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -68,14 +83,19 @@ declare const BrandFooter: vue.DefineComponent<vue.ExtractPropTypes<{
68
83
  type: PropType<string | null>;
69
84
  default: null;
70
85
  };
86
+ linkFactory: {
87
+ type: PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>;
88
+ default: null;
89
+ };
71
90
  }>> & Readonly<{}>, {
72
91
  theme: BrandTheme | null;
73
92
  shellClass: string | null;
93
+ linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;
74
94
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
75
95
  declare function registerBrandShellVueElements(): {
76
96
  headerTagName: string;
77
97
  footerTagName: string;
78
98
  };
79
99
  //#endregion
80
- export { type BrandDetails, BrandFooter, BrandHeader, BrandShellVueProps, type BrandTheme, registerBrandShellVueElements };
100
+ export { type BrandDetails, BrandFooter, BrandHeader, BrandShellVueProps, type BrandTheme, type LinkFactoryOptions, registerBrandShellVueElements };
81
101
  //# sourceMappingURL=vue.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vue.d.mts","names":[],"sources":["../src/vue/index.ts"],"mappings":";;;;;UAQiB,kBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;AAAA;AAAA,cAmEW,WAAA,MAAW,eAAA,CA1CQ,GAAA,CA0CR,gBAAA;;UAlDA,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;oBA0CA,GAAA,CAAA,YAAA;;4GA1CQ,GAAA,CAAA,gBAAA;;UARR,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;;;;cA2CX,WAAA,MAAW,eAAA,CA3CQ,GAAA,CA2CR,gBAAA;;UAnDA,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;oBA2CA,GAAA,CAAA,YAAA;;4GA3CQ,GAAA,CAAA,gBAAA;;UARR,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;;;;iBA6CR,6BAAA,CAAA;EAA6B,aAAA;EAAA,aAAA;AAAA"}
1
+ {"version":3,"file":"vue.d.mts","names":[],"sources":["../src/vue/index.ts"],"mappings":";;;;;;UASiB,kBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,cAwEpC,WAAA,MAAW,eAAA,CA3CqB,GAAA,CA2CrB,gBAAA;;UAvDA,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;oBA2CxD,GAAA,CAAA,YAAA;;4GA3CqB,GAAA,CAAA,gBAAA;;UAZrB,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;;;;0BAAvB,kBAAA,KAAuB,iBAAA;AAAA,uBAAiB,GAAA,CAAA,uBAAA;AAAA,cA4CpF,WAAA,MAAW,eAAA,CA5CqB,GAAA,CA4CrB,gBAAA;;UAxDA,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;oBA4CxD,GAAA,CAAA,YAAA;;4GA5CqB,GAAA,CAAA,gBAAA;;UAZrB,QAAA,CAAS,YAAA;;;;UAIT,QAAA,CAAS,UAAA;;;;UAIT,QAAA;;;;UAIa,QAAA,GAAW,OAAA,EAAS,kBAAA,KAAuB,iBAAA;;;;;;0BAAvB,kBAAA,KAAuB,iBAAA;AAAA,uBAAiB,GAAA,CAAA,uBAAA;AAAA,iBA8CjF,6BAAA,CAAA;EAA6B,aAAA;EAAA,aAAA;AAAA"}
package/dist/vue.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { l as shouldValidateInDev, n as assertValidBrandDetails, r as assertValidBrandTheme } from "./validation-xdqzwr3p.mjs";
1
+ import { l as shouldValidateInDev, n as assertValidBrandDetails, r as assertValidBrandTheme } from "./validation-CtH2UkVv.mjs";
2
2
  import { applyBrandShellProps, registerBrandShellElements } from "./web.mjs";
3
3
  import { defineComponent, h, onMounted, ref, watch } from "vue";
4
4
 
@@ -21,6 +21,10 @@ function createBrandShellVueComponent(tagName, componentName) {
21
21
  shellClass: {
22
22
  type: String,
23
23
  default: null
24
+ },
25
+ linkFactory: {
26
+ type: Function,
27
+ default: null
24
28
  }
25
29
  },
26
30
  setup(props) {
@@ -34,7 +38,8 @@ function createBrandShellVueComponent(tagName, componentName) {
34
38
  applyBrandShellProps(elementRef.value, {
35
39
  details: props.details,
36
40
  theme: props.theme ?? null,
37
- shellClass: props.shellClass ?? null
41
+ shellClass: props.shellClass ?? null,
42
+ linkFactory: props.linkFactory ?? void 0
38
43
  });
39
44
  };
40
45
  onMounted(() => {
@@ -43,7 +48,8 @@ function createBrandShellVueComponent(tagName, componentName) {
43
48
  watch(() => [
44
49
  props.details,
45
50
  props.theme,
46
- props.shellClass
51
+ props.shellClass,
52
+ props.linkFactory
47
53
  ], () => {
48
54
  syncProps();
49
55
  }, { deep: true });
package/dist/vue.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vue.mjs","names":[],"sources":["../src/vue/index.ts"],"sourcesContent":["import { defineComponent, h, onMounted, ref, watch, type PropType } from \"vue\";\n\nimport type { BrandDetails, BrandTheme } from \"../core\";\nimport { assertValidBrandDetails, assertValidBrandTheme, shouldValidateInDev } from \"../core\";\nimport { applyBrandShellProps, registerBrandShellElements, type BrandShellElementLike } from \"../web\";\n\nexport type { BrandDetails, BrandTheme } from \"../core\";\n\nexport interface BrandShellVueProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n}\n\nfunction ensureBrandShellElementsRegistered() {\n if (typeof customElements !== \"undefined\") {\n registerBrandShellElements();\n }\n}\n\nfunction createBrandShellVueComponent(\n tagName: \"brand-header\" | \"brand-footer\",\n componentName: \"BrandHeader\" | \"BrandFooter\",\n) {\n return defineComponent({\n name: componentName,\n props: {\n details: {\n type: Object as PropType<BrandDetails>,\n required: true,\n },\n theme: {\n type: Object as PropType<BrandTheme | null>,\n default: null,\n },\n shellClass: {\n type: String as PropType<string | null>,\n default: null,\n },\n },\n setup(props) {\n ensureBrandShellElementsRegistered();\n\n const elementRef = ref<BrandShellElementLike | null>(null);\n\n const syncProps = () => {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, `brand-shell/vue ${componentName} details`);\n assertValidBrandTheme(props.theme, `brand-shell/vue ${componentName} theme`);\n }\n\n applyBrandShellProps(elementRef.value, {\n details: props.details,\n theme: props.theme ?? null,\n shellClass: props.shellClass ?? null,\n });\n };\n\n onMounted(() => {\n syncProps();\n });\n\n watch(\n () => [props.details, props.theme, props.shellClass],\n () => {\n syncProps();\n },\n { deep: true },\n );\n\n return () =>\n h(tagName, {\n ref: elementRef,\n });\n },\n });\n}\n\nexport const BrandHeader = createBrandShellVueComponent(\"brand-header\", \"BrandHeader\");\nexport const BrandFooter = createBrandShellVueComponent(\"brand-footer\", \"BrandFooter\");\n\nexport function registerBrandShellVueElements() {\n return registerBrandShellElements();\n}\n"],"mappings":";;;;;AAcA,SAAS,qCAAqC;AAC5C,KAAI,OAAO,mBAAmB,YAC5B,6BAA4B;;AAIhC,SAAS,6BACP,SACA,eACA;AACA,QAAO,gBAAgB;EACrB,MAAM;EACN,OAAO;GACL,SAAS;IACP,MAAM;IACN,UAAU;IACX;GACD,OAAO;IACL,MAAM;IACN,SAAS;IACV;GACD,YAAY;IACV,MAAM;IACN,SAAS;IACV;GACF;EACD,MAAM,OAAO;AACX,uCAAoC;GAEpC,MAAM,aAAa,IAAkC,KAAK;GAE1D,MAAM,kBAAkB;AACtB,QAAI,qBAAqB,EAAE;AACzB,6BAAwB,MAAM,SAAS,mBAAmB,cAAc,UAAU;AAClF,2BAAsB,MAAM,OAAO,mBAAmB,cAAc,QAAQ;;AAG9E,yBAAqB,WAAW,OAAO;KACrC,SAAS,MAAM;KACf,OAAO,MAAM,SAAS;KACtB,YAAY,MAAM,cAAc;KACjC,CAAC;;AAGJ,mBAAgB;AACd,eAAW;KACX;AAEF,eACQ;IAAC,MAAM;IAAS,MAAM;IAAO,MAAM;IAAW,QAC9C;AACJ,eAAW;MAEb,EAAE,MAAM,MAAM,CACf;AAED,gBACE,EAAE,SAAS,EACT,KAAK,YACN,CAAC;;EAEP,CAAC;;AAGJ,MAAa,cAAc,6BAA6B,gBAAgB,cAAc;AACtF,MAAa,cAAc,6BAA6B,gBAAgB,cAAc;AAEtF,SAAgB,gCAAgC;AAC9C,QAAO,4BAA4B"}
1
+ {"version":3,"file":"vue.mjs","names":[],"sources":["../src/vue/index.ts"],"sourcesContent":["import { defineComponent, h, onMounted, ref, watch, type PropType } from \"vue\";\n\nimport type { BrandDetails, BrandTheme } from \"../core\";\nimport { assertValidBrandDetails, assertValidBrandTheme, shouldValidateInDev } from \"../core\";\nimport { applyBrandShellProps, registerBrandShellElements, type BrandShellElementLike, type LinkFactoryOptions } from \"../web\";\n\nexport type { BrandDetails, BrandTheme } from \"../core\";\nexport type { LinkFactoryOptions } from \"../web\";\n\nexport interface BrandShellVueProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;\n}\n\nfunction ensureBrandShellElementsRegistered() {\n if (typeof customElements !== \"undefined\") {\n registerBrandShellElements();\n }\n}\n\nfunction createBrandShellVueComponent(\n tagName: \"brand-header\" | \"brand-footer\",\n componentName: \"BrandHeader\" | \"BrandFooter\",\n) {\n return defineComponent({\n name: componentName,\n props: {\n details: {\n type: Object as PropType<BrandDetails>,\n required: true,\n },\n theme: {\n type: Object as PropType<BrandTheme | null>,\n default: null,\n },\n shellClass: {\n type: String as PropType<string | null>,\n default: null,\n },\n linkFactory: {\n type: Function as unknown as PropType<((options: LinkFactoryOptions) => HTMLAnchorElement) | null>,\n default: null,\n },\n },\n setup(props) {\n ensureBrandShellElementsRegistered();\n\n const elementRef = ref<BrandShellElementLike | null>(null);\n\n const syncProps = () => {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, `brand-shell/vue ${componentName} details`);\n assertValidBrandTheme(props.theme, `brand-shell/vue ${componentName} theme`);\n }\n\n applyBrandShellProps(elementRef.value, {\n details: props.details,\n theme: props.theme ?? null,\n shellClass: props.shellClass ?? null,\n linkFactory: props.linkFactory ?? undefined,\n });\n };\n\n onMounted(() => {\n syncProps();\n });\n\n watch(\n () => [props.details, props.theme, props.shellClass, props.linkFactory],\n () => {\n syncProps();\n },\n { deep: true },\n );\n\n return () =>\n h(tagName, {\n ref: elementRef,\n });\n },\n });\n}\n\nexport const BrandHeader = createBrandShellVueComponent(\"brand-header\", \"BrandHeader\");\nexport const BrandFooter = createBrandShellVueComponent(\"brand-footer\", \"BrandFooter\");\n\nexport function registerBrandShellVueElements() {\n return registerBrandShellElements();\n}\n"],"mappings":";;;;;AAgBA,SAAS,qCAAqC;AAC5C,KAAI,OAAO,mBAAmB,YAC5B,6BAA4B;;AAIhC,SAAS,6BACP,SACA,eACA;AACA,QAAO,gBAAgB;EACrB,MAAM;EACN,OAAO;GACL,SAAS;IACP,MAAM;IACN,UAAU;IACX;GACD,OAAO;IACL,MAAM;IACN,SAAS;IACV;GACD,YAAY;IACV,MAAM;IACN,SAAS;IACV;GACD,aAAa;IACX,MAAM;IACN,SAAS;IACV;GACF;EACD,MAAM,OAAO;AACX,uCAAoC;GAEpC,MAAM,aAAa,IAAkC,KAAK;GAE1D,MAAM,kBAAkB;AACtB,QAAI,qBAAqB,EAAE;AACzB,6BAAwB,MAAM,SAAS,mBAAmB,cAAc,UAAU;AAClF,2BAAsB,MAAM,OAAO,mBAAmB,cAAc,QAAQ;;AAG9E,yBAAqB,WAAW,OAAO;KACrC,SAAS,MAAM;KACf,OAAO,MAAM,SAAS;KACtB,YAAY,MAAM,cAAc;KAChC,aAAa,MAAM,eAAe;KACnC,CAAC;;AAGJ,mBAAgB;AACd,eAAW;KACX;AAEF,eACQ;IAAC,MAAM;IAAS,MAAM;IAAO,MAAM;IAAY,MAAM;IAAY,QACjE;AACJ,eAAW;MAEb,EAAE,MAAM,MAAM,CACf;AAED,gBACE,EAAE,SAAS,EACT,KAAK,YACN,CAAC;;EAEP,CAAC;;AAGJ,MAAa,cAAc,6BAA6B,gBAAgB,cAAc;AACtF,MAAa,cAAc,6BAA6B,gBAAgB,cAAc;AAEtF,SAAgB,gCAAgC;AAC9C,QAAO,4BAA4B"}
package/dist/web.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as BrandTheme, n as BrandDetails, r as BrandNavLink, t as BrandAction } from "./types-PQziYg7Z.mjs";
1
+ import { i as BrandTheme, n as BrandDetails, r as BrandNavLink, t as BrandAction } from "./types-B_CLRZMO.mjs";
2
2
 
3
3
  //#region src/web/index.d.ts
4
4
  declare const HTMLElementBase: typeof HTMLElement;
@@ -6,15 +6,24 @@ interface RegisterBrandShellElementsOptions {
6
6
  headerTagName?: string;
7
7
  footerTagName?: string;
8
8
  }
9
+ interface LinkFactoryOptions {
10
+ href: string;
11
+ className: string;
12
+ ariaLabel: string;
13
+ target: string;
14
+ rel?: string;
15
+ }
9
16
  interface BrandShellElementProps {
10
17
  details: BrandDetails;
11
18
  theme?: BrandTheme | null;
12
19
  shellClass?: string | null;
20
+ linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;
13
21
  }
14
22
  type BrandShellElementLike = HTMLElement & {
15
23
  details?: BrandDetails | null;
16
24
  theme?: BrandTheme | null;
17
25
  shellClass?: string | null;
26
+ linkFactory?: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;
18
27
  };
19
28
  interface SerializedBrandShellAttributes {
20
29
  details: string;
@@ -26,23 +35,26 @@ declare abstract class BaseBrandShellElement extends HTMLElementBase {
26
35
  private _details;
27
36
  private _theme;
28
37
  private _shellClass;
38
+ private _linkFactory;
29
39
  get details(): BrandDetails | null;
30
40
  set details(value: BrandDetails | null);
31
41
  get theme(): BrandTheme | null;
32
42
  set theme(value: BrandTheme | null);
33
43
  get shellClass(): string | null;
34
44
  set shellClass(value: string | null);
45
+ get linkFactory(): ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;
46
+ set linkFactory(value: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null);
35
47
  connectedCallback(): void;
36
48
  attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void;
37
- protected abstract build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;
49
+ protected abstract build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null, linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null): HTMLElement;
38
50
  private render;
39
51
  private upgradeProperty;
40
52
  }
41
53
  declare class BrandHeaderElement extends BaseBrandShellElement {
42
- protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;
54
+ protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null, linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null): HTMLElement;
43
55
  }
44
56
  declare class BrandFooterElement extends BaseBrandShellElement {
45
- protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null): HTMLElement;
57
+ protected build(details: BrandDetails, theme: BrandTheme | null, shellClass: string | null, linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null): HTMLElement;
46
58
  }
47
59
  declare function registerBrandShellElements(options?: RegisterBrandShellElementsOptions): {
48
60
  headerTagName: string;
@@ -57,5 +69,5 @@ declare global {
57
69
  }
58
70
  } //# sourceMappingURL=index.d.ts.map
59
71
  //#endregion
60
- export { type BrandAction, type BrandDetails, BrandFooterElement, BrandHeaderElement, type BrandNavLink, BrandShellElementLike, BrandShellElementProps, type BrandTheme, RegisterBrandShellElementsOptions, SerializedBrandShellAttributes, applyBrandShellProps, registerBrandShellElements, serializeBrandShellAttributes };
72
+ export { type BrandAction, type BrandDetails, BrandFooterElement, BrandHeaderElement, type BrandNavLink, BrandShellElementLike, BrandShellElementProps, type BrandTheme, LinkFactoryOptions, RegisterBrandShellElementsOptions, SerializedBrandShellAttributes, applyBrandShellProps, registerBrandShellElements, serializeBrandShellAttributes };
61
73
  //# sourceMappingURL=web.d.mts.map
@@ -1 +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"}
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,kBAAA;EACf,IAAA;EACA,SAAA;EACA,SAAA;EACA,MAAA;EACA,GAAA;AAAA;AAAA,UAGe,sBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,KAGrC,qBAAA,GAAwB,WAAA;EAClC,OAAA,GAAU,YAAA;EACV,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,KAAgB,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,UAGjC,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,QACA,YAAA;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;EAAA,IAKX,WAAA,CAAA,KAAiB,OAAA,EAAS,kBAAA,KAAuB,iBAAA;EAAA,IAIjD,WAAA,CAAY,KAAA,IAAS,OAAA,EAAS,kBAAA,KAAuB,iBAAA;EAKzD,iBAAA,CAAA;EAeA,wBAAA,CAAyB,IAAA,UAAc,SAAA,iBAA0B,QAAA;EAAA,mBAW9C,KAAA,CACjB,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;EAAA,QAEK,MAAA;EAAA,QA2BA,eAAA;AAAA;AAAA,cASG,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CACR,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;AAAA;AAAA,cAKQ,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CACR,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;AAAA;AAAA,iBAKW,0BAAA,CAA2B,OAAA,GAAS,iCAAA;;;;iBAsBpC,oBAAA,CACd,OAAA,EAAS,qBAAA,qBACT,KAAA,EAAO,sBAAA;AAAA,iBAaO,6BAAA,CAA8B,KAAA,EAAO,sBAAA,GAAyB,8BAAA;AAAA,QAwXtE,MAAA;EAAA,UACI,qBAAA;IACR,cAAA,EAAgB,kBAAA;IAChB,cAAA,EAAgB,kBAAA;EAAA;AAAA"}