platform-types 1.10.674 → 1.10.676
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/cjs/demo.js +1 -1
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/platform.js +0 -4
- package/dist/cjs/storefrontDesign.js +118 -0
- package/dist/cjs/storefrontDesignRegistry.js +130 -0
- package/dist/cjs/storefrontTheme.js +102 -0
- package/dist/demo.d.ts +1 -1
- package/dist/demo.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/platform.d.ts +2 -1
- package/dist/platform.js +0 -4
- package/dist/store.d.ts +2 -0
- package/dist/storefrontDesign.d.ts +331 -0
- package/dist/storefrontDesign.js +114 -0
- package/dist/storefrontDesignRegistry.d.ts +769 -0
- package/dist/storefrontDesignRegistry.js +122 -0
- package/dist/storefrontTheme.d.ts +54 -0
- package/dist/storefrontTheme.js +97 -0
- package/package.json +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const DESIGN_METADATA = {
|
|
2
|
+
scope: 'storefront',
|
|
3
|
+
allowedLayers: ['GLOBAL', 'WEB', 'WEB#store'],
|
|
4
|
+
audience: 'public',
|
|
5
|
+
lifecycle: 'release-owned',
|
|
6
|
+
storage: { policy: 'GLOBALS', override: 'design.settings' },
|
|
7
|
+
ownership: 'storefront-design',
|
|
8
|
+
};
|
|
9
|
+
const color = (key, defaultValue, label, help) => ({
|
|
10
|
+
...DESIGN_METADATA,
|
|
11
|
+
key,
|
|
12
|
+
defaultValue,
|
|
13
|
+
valueType: 'string',
|
|
14
|
+
valueSchema: { type: 'string', format: 'hex-color', maxLength: 7 },
|
|
15
|
+
maxLength: 7,
|
|
16
|
+
description: help,
|
|
17
|
+
literal: { kind: 'text', label, help },
|
|
18
|
+
});
|
|
19
|
+
const choice = (key, allowedValues, defaultValue, label, help) => ({
|
|
20
|
+
...DESIGN_METADATA,
|
|
21
|
+
key,
|
|
22
|
+
defaultValue,
|
|
23
|
+
valueType: 'string',
|
|
24
|
+
allowedValues,
|
|
25
|
+
valueSchema: { type: 'string', enum: allowedValues },
|
|
26
|
+
description: help,
|
|
27
|
+
literal: { kind: 'text', label, help },
|
|
28
|
+
});
|
|
29
|
+
const toggle = (key, defaultValue, label, help) => ({
|
|
30
|
+
...DESIGN_METADATA,
|
|
31
|
+
key,
|
|
32
|
+
defaultValue,
|
|
33
|
+
valueType: 'boolean',
|
|
34
|
+
valueSchema: { type: 'boolean' },
|
|
35
|
+
description: help,
|
|
36
|
+
literal: { kind: 'text', label, help },
|
|
37
|
+
});
|
|
38
|
+
export const STOREFRONT_DESIGN_SETTINGS = {
|
|
39
|
+
primaryColor: color('primaryColor', '#0066CC', 'Color principal', 'Color solicitado para los elementos principales de la tienda.'),
|
|
40
|
+
accentColor: color('accentColor', '#0066CC', 'Color de acento', 'Color solicitado para los elementos destacados de la tienda.'),
|
|
41
|
+
backgroundColor: color('backgroundColor', '#FFFFFF', 'Color de fondo', 'Color solicitado para el fondo de la tienda.'),
|
|
42
|
+
headerColor: color('headerColor', '#0066CC', 'Color del encabezado', 'Color solicitado para el encabezado de la tienda.'),
|
|
43
|
+
footerColor: color('footerColor', '#212529', 'Color del pie', 'Color solicitado para el pie de la tienda.'),
|
|
44
|
+
headingFont: choice('headingFont', ['ubuntu', 'noto-sans', 'noto-serif'], 'ubuntu', 'Tipografía de títulos', 'Familia tipográfica de los títulos.'),
|
|
45
|
+
bodyFont: choice('bodyFont', ['ubuntu', 'noto-sans', 'noto-serif'], 'ubuntu', 'Tipografía de texto', 'Familia tipográfica del cuerpo de texto.'),
|
|
46
|
+
textSize: choice('textSize', ['compact', 'standard', 'large'], 'standard', 'Tamaño de texto', 'Escala de texto de la tienda.'),
|
|
47
|
+
cardImageRatio: choice('cardImageRatio', ['square', 'portrait'], 'square', 'Proporción de imágenes', 'Proporción de las imágenes en las tarjetas de productos.'),
|
|
48
|
+
cardDensity: choice('cardDensity', ['compact', 'spacious'], 'spacious', 'Espaciado de tarjetas', 'Espaciado entre los elementos de las tarjetas.'),
|
|
49
|
+
showBrand: toggle('showBrand', false, 'Mostrar marca', 'Mostrar la marca en las tarjetas de productos.'),
|
|
50
|
+
showSku: toggle('showSku', false, 'Mostrar SKU', 'Mostrar el código SKU en las tarjetas de productos.'),
|
|
51
|
+
};
|
|
52
|
+
export const isStorefrontDesignSettingKey = (key) => Object.hasOwn(STOREFRONT_DESIGN_SETTINGS, key);
|
|
53
|
+
export const validateStorefrontSettingValue = (schema, value) => {
|
|
54
|
+
if (schema.type === 'boolean')
|
|
55
|
+
return typeof value === 'boolean';
|
|
56
|
+
if (typeof value !== 'string' || !value.isWellFormed())
|
|
57
|
+
return false;
|
|
58
|
+
if (schema.maxLength !== undefined && Array.from(value).length > schema.maxLength)
|
|
59
|
+
return false;
|
|
60
|
+
if (schema.format === 'hex-color' && !/^#[0-9a-fA-F]{6}$/.test(value))
|
|
61
|
+
return false;
|
|
62
|
+
return schema.enum === undefined || schema.enum.includes(value);
|
|
63
|
+
};
|
|
64
|
+
export const validateStorefrontDesignSetting = (key, value) => isStorefrontDesignSettingKey(key) &&
|
|
65
|
+
validateStorefrontSettingValue(STOREFRONT_DESIGN_SETTINGS[key].valueSchema, value);
|
|
66
|
+
export const STOREFRONT_DESIGN_LITERAL_MAX_CODE_POINTS = 4096;
|
|
67
|
+
export const resolveStorefrontDesignSettingsLayers = (layers, overrides = {}) => {
|
|
68
|
+
const values = {};
|
|
69
|
+
const provenance = {};
|
|
70
|
+
const ordered = ['GLOBAL', 'WEB', 'WEB#store'].flatMap((name) => layers.filter(({ layer }) => layer === name));
|
|
71
|
+
for (const key of Object.keys(STOREFRONT_DESIGN_SETTINGS)) {
|
|
72
|
+
Object.assign(values, { [key]: STOREFRONT_DESIGN_SETTINGS[key].defaultValue });
|
|
73
|
+
provenance[key] = 'default';
|
|
74
|
+
for (const { layer, values: inherited } of ordered) {
|
|
75
|
+
if (!Object.hasOwn(inherited, key) || !validateStorefrontDesignSetting(key, inherited[key]))
|
|
76
|
+
continue;
|
|
77
|
+
Object.assign(values, { [key]: inherited[key] });
|
|
78
|
+
provenance[key] = layer;
|
|
79
|
+
}
|
|
80
|
+
if (Object.hasOwn(overrides, key) && validateStorefrontDesignSetting(key, overrides[key])) {
|
|
81
|
+
Object.assign(values, { [key]: overrides[key] });
|
|
82
|
+
provenance[key] = 'design';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { values, provenance };
|
|
86
|
+
};
|
|
87
|
+
export const resolveStorefrontDesignSettings = (overrides = {}, layers = []) => resolveStorefrontDesignSettingsLayers(layers, overrides).values;
|
|
88
|
+
const LIVE_METADATA = {
|
|
89
|
+
allowedLayers: ['GLOBAL', 'WEB', 'WEB#store'],
|
|
90
|
+
audience: 'public',
|
|
91
|
+
lifecycle: 'live',
|
|
92
|
+
ownership: 'storefront-operation',
|
|
93
|
+
excludedFromSnapshot: true,
|
|
94
|
+
};
|
|
95
|
+
export const STOREFRONT_DESIGN_REGISTRY = {
|
|
96
|
+
settings: STOREFRONT_DESIGN_SETTINGS,
|
|
97
|
+
content: {
|
|
98
|
+
schema: 'StorefrontDesignV1',
|
|
99
|
+
lifecycle: 'release-owned',
|
|
100
|
+
ownership: 'storefront-design',
|
|
101
|
+
audience: 'public',
|
|
102
|
+
storage: { document: 'storefront-design' },
|
|
103
|
+
},
|
|
104
|
+
literalOverrides: {
|
|
105
|
+
catalog: 'WEB_LITERAL_DEFAULTS',
|
|
106
|
+
allowedLayers: DESIGN_METADATA.allowedLayers,
|
|
107
|
+
lifecycle: 'release-owned',
|
|
108
|
+
ownership: 'storefront-design',
|
|
109
|
+
audience: 'public',
|
|
110
|
+
storage: { source: 'LITERALS', override: 'design.literalOverrides' },
|
|
111
|
+
},
|
|
112
|
+
live: {
|
|
113
|
+
showIvaAlert: { ...LIVE_METADATA, storage: { source: 'featureFlags' } },
|
|
114
|
+
showCurrencySelector: { ...LIVE_METADATA, storage: { source: 'featureFlags' } },
|
|
115
|
+
enableSaveForLater: { ...LIVE_METADATA, storage: { source: 'featureFlags' } },
|
|
116
|
+
prices: { ...LIVE_METADATA, storage: { source: 'catalogue' } },
|
|
117
|
+
stock: { ...LIVE_METADATA, storage: { source: 'catalogue' } },
|
|
118
|
+
checkout: { ...LIVE_METADATA, storage: { source: 'store' } },
|
|
119
|
+
catalogue: { ...LIVE_METADATA, storage: { source: 'catalogue' } },
|
|
120
|
+
availability: { ...LIVE_METADATA, storage: { source: 'store' } },
|
|
121
|
+
},
|
|
122
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type StorefrontDesignSettings } from './storefrontDesignRegistry.ts';
|
|
2
|
+
type StorefrontThemeMode = 'light' | 'dark';
|
|
3
|
+
type StorefrontThemeSurfaceId = 'page' | 'header' | 'footer';
|
|
4
|
+
export interface StorefrontThemeSurface {
|
|
5
|
+
background: string;
|
|
6
|
+
text: string;
|
|
7
|
+
secondaryText: string;
|
|
8
|
+
link: string;
|
|
9
|
+
focus: string;
|
|
10
|
+
border: string;
|
|
11
|
+
button: {
|
|
12
|
+
background: string;
|
|
13
|
+
text: string;
|
|
14
|
+
hover: string;
|
|
15
|
+
hoverText: string;
|
|
16
|
+
};
|
|
17
|
+
disabled: {
|
|
18
|
+
background: string;
|
|
19
|
+
text: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface StorefrontThemeWarning {
|
|
23
|
+
mode: StorefrontThemeMode;
|
|
24
|
+
surface: StorefrontThemeSurfaceId;
|
|
25
|
+
role: 'link' | 'button' | 'focus';
|
|
26
|
+
requested: string;
|
|
27
|
+
effective: string;
|
|
28
|
+
}
|
|
29
|
+
export interface StorefrontResolvedTheme {
|
|
30
|
+
requested: StorefrontDesignSettings;
|
|
31
|
+
light: Record<StorefrontThemeSurfaceId, StorefrontThemeSurface>;
|
|
32
|
+
dark: Record<StorefrontThemeSurfaceId, StorefrontThemeSurface>;
|
|
33
|
+
typography: {
|
|
34
|
+
headingFont: StorefrontDesignSettings['headingFont'];
|
|
35
|
+
bodyFont: StorefrontDesignSettings['bodyFont'];
|
|
36
|
+
fontSize: number;
|
|
37
|
+
body1: number;
|
|
38
|
+
body2: number;
|
|
39
|
+
h1: number;
|
|
40
|
+
h2: number;
|
|
41
|
+
h3: number;
|
|
42
|
+
h4: number;
|
|
43
|
+
h5: number;
|
|
44
|
+
h6: number;
|
|
45
|
+
};
|
|
46
|
+
shape: {
|
|
47
|
+
borderRadius: number;
|
|
48
|
+
};
|
|
49
|
+
spacing: number;
|
|
50
|
+
warnings: StorefrontThemeWarning[];
|
|
51
|
+
}
|
|
52
|
+
export declare const storefrontContrastRatio: (foreground: string, background: string) => number;
|
|
53
|
+
export declare const resolveStorefrontTheme: (overrides?: Partial<StorefrontDesignSettings>) => StorefrontResolvedTheme;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { resolveStorefrontDesignSettings } from "./storefrontDesignRegistry.js";
|
|
2
|
+
const channels = (color) => [
|
|
3
|
+
Number.parseInt(color.slice(1, 3), 16),
|
|
4
|
+
Number.parseInt(color.slice(3, 5), 16),
|
|
5
|
+
Number.parseInt(color.slice(5, 7), 16),
|
|
6
|
+
];
|
|
7
|
+
const hex = (rgb) => `#${rgb
|
|
8
|
+
.map((value) => Math.round(value).toString(16).padStart(2, '0'))
|
|
9
|
+
.join('')
|
|
10
|
+
.toUpperCase()}`;
|
|
11
|
+
// MUI's RGB lighten/darken arithmetic; fixed hex output keeps tokens CSS-independent.
|
|
12
|
+
const mix = (color, target, amount) => {
|
|
13
|
+
const destination = channels(target);
|
|
14
|
+
return hex(channels(color).map((value, index) => value + (destination[index] - value) * amount));
|
|
15
|
+
};
|
|
16
|
+
const luminance = (color) => {
|
|
17
|
+
const linear = channels(color).map((channel) => {
|
|
18
|
+
const value = channel / 255;
|
|
19
|
+
return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
20
|
+
});
|
|
21
|
+
return linear[0] * 0.2126 + linear[1] * 0.7152 + linear[2] * 0.0722;
|
|
22
|
+
};
|
|
23
|
+
export const storefrontContrastRatio = (foreground, background) => {
|
|
24
|
+
const a = luminance(foreground);
|
|
25
|
+
const b = luminance(background);
|
|
26
|
+
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
27
|
+
};
|
|
28
|
+
const readableText = (background) => storefrontContrastRatio('#000000', background) >= storefrontContrastRatio('#FFFFFF', background)
|
|
29
|
+
? '#000000'
|
|
30
|
+
: '#FFFFFF';
|
|
31
|
+
const ensureContrast = (color, background, minimum) => {
|
|
32
|
+
if (storefrontContrastRatio(color, background) >= minimum)
|
|
33
|
+
return color.toUpperCase();
|
|
34
|
+
const target = readableText(background);
|
|
35
|
+
for (let step = 1; step <= 100; step++) {
|
|
36
|
+
const candidate = mix(color, target, step / 100);
|
|
37
|
+
if (storefrontContrastRatio(candidate, background) >= minimum)
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
return target;
|
|
41
|
+
};
|
|
42
|
+
const surfaceTokens = (background, settings, mode, surface, warnings) => {
|
|
43
|
+
const adjusted = (role, requested, minimum) => {
|
|
44
|
+
const effective = ensureContrast(requested, background, minimum);
|
|
45
|
+
if (effective !== requested.toUpperCase())
|
|
46
|
+
warnings.push({ mode, surface, role, requested, effective });
|
|
47
|
+
return effective;
|
|
48
|
+
};
|
|
49
|
+
const text = readableText(background);
|
|
50
|
+
const button = adjusted('button', settings.primaryColor, 3);
|
|
51
|
+
const hover = ensureContrast(mix(button, readableText(background), 0.2), background, 3);
|
|
52
|
+
const disabledBackground = mix(background, text, 0.12);
|
|
53
|
+
return {
|
|
54
|
+
background,
|
|
55
|
+
text,
|
|
56
|
+
secondaryText: ensureContrast(mix(background, text, 0.6), background, 4.5),
|
|
57
|
+
link: adjusted('link', settings.primaryColor, 4.5),
|
|
58
|
+
focus: adjusted('focus', settings.accentColor, 3),
|
|
59
|
+
border: ensureContrast(mix(background, text, 0.3), background, 3),
|
|
60
|
+
button: { background: button, text: readableText(button), hover, hoverText: readableText(hover) },
|
|
61
|
+
disabled: { background: disabledBackground, text: readableText(disabledBackground) },
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
export const resolveStorefrontTheme = (overrides = {}) => {
|
|
65
|
+
const settings = resolveStorefrontDesignSettings(overrides);
|
|
66
|
+
const warnings = [];
|
|
67
|
+
const makeMode = (mode) => {
|
|
68
|
+
const background = (requested) => mode === 'light' ? requested.toUpperCase() : mix(requested, '#000000', 0.9);
|
|
69
|
+
return {
|
|
70
|
+
page: surfaceTokens(background(settings.backgroundColor), settings, mode, 'page', warnings),
|
|
71
|
+
header: surfaceTokens(background(settings.headerColor), settings, mode, 'header', warnings),
|
|
72
|
+
footer: surfaceTokens(background(settings.footerColor), settings, mode, 'footer', warnings),
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const scale = { compact: 0.875, standard: 1, large: 1.125 }[settings.textSize];
|
|
76
|
+
return {
|
|
77
|
+
requested: settings,
|
|
78
|
+
light: makeMode('light'),
|
|
79
|
+
dark: makeMode('dark'),
|
|
80
|
+
typography: {
|
|
81
|
+
headingFont: settings.headingFont,
|
|
82
|
+
bodyFont: settings.bodyFont,
|
|
83
|
+
fontSize: 14 * scale,
|
|
84
|
+
body1: 16 * scale,
|
|
85
|
+
body2: 14 * scale,
|
|
86
|
+
h1: 96 * scale,
|
|
87
|
+
h2: 60 * scale,
|
|
88
|
+
h3: 48 * scale,
|
|
89
|
+
h4: 34 * scale,
|
|
90
|
+
h5: 24 * scale,
|
|
91
|
+
h6: 20 * scale,
|
|
92
|
+
},
|
|
93
|
+
shape: { borderRadius: 4 },
|
|
94
|
+
spacing: 8,
|
|
95
|
+
warnings,
|
|
96
|
+
};
|
|
97
|
+
};
|