@aiaiai-pt/design-system 0.4.4 → 0.5.1
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/components/Button.svelte +4 -0
- package/components/Calendar.svelte +971 -0
- package/components/Calendar.svelte.d.ts +50 -0
- package/components/DatePicker.svelte +473 -0
- package/components/DatePicker.svelte.d.ts +59 -0
- package/components/DateRangePicker.svelte +558 -0
- package/components/DateRangePicker.svelte.d.ts +55 -0
- package/components/DateTimePicker.svelte +275 -0
- package/components/DateTimePicker.svelte.d.ts +55 -0
- package/components/MapCluster.svelte +220 -0
- package/components/MapCluster.svelte.d.ts +39 -0
- package/components/MapDisplay.svelte +139 -0
- package/components/MapDisplay.svelte.d.ts +35 -0
- package/components/MapHeatmap.svelte +164 -0
- package/components/MapHeatmap.svelte.d.ts +50 -0
- package/components/MapPicker.svelte +243 -0
- package/components/MapPicker.svelte.d.ts +49 -0
- package/components/MapPopup.svelte +101 -0
- package/components/MapPopup.svelte.d.ts +30 -0
- package/components/Select.svelte +3 -4
- package/components/StatCard.svelte +195 -0
- package/components/StatCard.svelte.d.ts +42 -0
- package/components/StatGrid.svelte +39 -0
- package/components/StatGrid.svelte.d.ts +29 -0
- package/components/index.d.ts +12 -0
- package/components/index.js +17 -0
- package/components/map-utils.d.ts +100 -0
- package/components/map-utils.js +338 -0
- package/package.json +8 -1
- package/tokens/components.css +215 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for map components.
|
|
3
|
+
* Not exported from index.js — internal to map components only.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {{ type: 'osm' }} OsmSource
|
|
8
|
+
* @typedef {{ type: 'xyz', url: string, attributions?: string, maxZoom?: number }} XyzSource
|
|
9
|
+
* @typedef {{ type: 'stadia', layer: string, retina?: boolean, apiKey?: string }} StadiaSource
|
|
10
|
+
* @typedef {OsmSource | XyzSource | StadiaSource} TileSourceConfig
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates an OL tile layer from a config object.
|
|
15
|
+
*
|
|
16
|
+
* @param {TileSourceConfig} config
|
|
17
|
+
* @returns {Promise<import('ol/layer/Tile.js').default>}
|
|
18
|
+
*/
|
|
19
|
+
export async function createTileLayer(config) {
|
|
20
|
+
const { default: TileLayer } = await import("ol/layer/Tile.js");
|
|
21
|
+
|
|
22
|
+
if (config.type === "stadia") {
|
|
23
|
+
const { default: StadiaMaps } = await import("ol/source/StadiaMaps.js");
|
|
24
|
+
return new TileLayer({
|
|
25
|
+
source: new StadiaMaps({
|
|
26
|
+
layer: config.layer,
|
|
27
|
+
retina: config.retina ?? true,
|
|
28
|
+
apiKey: config.apiKey,
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (config.type === "xyz") {
|
|
34
|
+
const { default: XYZ } = await import("ol/source/XYZ.js");
|
|
35
|
+
return new TileLayer({
|
|
36
|
+
source: new XYZ({
|
|
37
|
+
url: config.url,
|
|
38
|
+
attributions: config.attributions,
|
|
39
|
+
maxZoom: config.maxZoom,
|
|
40
|
+
crossOrigin: "anonymous",
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { default: OSM } = await import("ol/source/OSM.js");
|
|
46
|
+
return new TileLayer({ source: new OSM() });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Theme-Reactive Style Factory ────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {object} MapStyles
|
|
53
|
+
* @property {import('ol/style/Style.js').default} marker
|
|
54
|
+
* @property {import('ol/style/Style.js').default} polygon
|
|
55
|
+
* @property {(size: number) => import('ol/style/Style.js').default} cluster
|
|
56
|
+
* @property {() => void} refresh — re-read CSS tokens, mutate styles in place
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates OL styles from DS tokens. Returns mutable style objects
|
|
61
|
+
* with a refresh() method that re-reads CSS vars and updates the
|
|
62
|
+
* styles in place — call after theme change, then source.changed().
|
|
63
|
+
*
|
|
64
|
+
* @param {Element} el
|
|
65
|
+
* @returns {Promise<MapStyles>}
|
|
66
|
+
*/
|
|
67
|
+
export async function createMapStyles(el) {
|
|
68
|
+
const [
|
|
69
|
+
{ default: Style },
|
|
70
|
+
{ default: CircleStyle },
|
|
71
|
+
{ default: Fill },
|
|
72
|
+
{ default: Stroke },
|
|
73
|
+
{ default: Text },
|
|
74
|
+
] = await Promise.all([
|
|
75
|
+
import("ol/style/Style.js"),
|
|
76
|
+
import("ol/style/Circle.js"),
|
|
77
|
+
import("ol/style/Fill.js"),
|
|
78
|
+
import("ol/style/Stroke.js"),
|
|
79
|
+
import("ol/style/Text.js"),
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
// Mutable fill/stroke refs — mutated in-place on refresh()
|
|
83
|
+
const markerFillObj = new Fill({ color: "" });
|
|
84
|
+
const markerStrokeObj = new Stroke({ color: "", width: 0 });
|
|
85
|
+
const polyFillObj = new Fill({ color: "" });
|
|
86
|
+
const polyStrokeObj = new Stroke({ color: "", width: 0 });
|
|
87
|
+
|
|
88
|
+
let markerRadiusVal = 8;
|
|
89
|
+
let clusterBaseRadiusVal = 16;
|
|
90
|
+
let clusterFillColor = "";
|
|
91
|
+
let clusterTextColor = "";
|
|
92
|
+
let clusterFontStr = "";
|
|
93
|
+
|
|
94
|
+
const marker = new Style({
|
|
95
|
+
image: new CircleStyle({
|
|
96
|
+
radius: 8,
|
|
97
|
+
fill: markerFillObj,
|
|
98
|
+
stroke: markerStrokeObj,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const polygon = new Style({
|
|
103
|
+
fill: polyFillObj,
|
|
104
|
+
stroke: polyStrokeObj,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** @type {Map<string, import('ol/style/Style.js').default>} */
|
|
108
|
+
const clusterCache = new Map();
|
|
109
|
+
|
|
110
|
+
function readTokens() {
|
|
111
|
+
markerFillObj.setColor(cssVar(el, "--map-marker-fill", "#ff6b35"));
|
|
112
|
+
markerStrokeObj.setColor(cssVar(el, "--map-marker-stroke", "#fff"));
|
|
113
|
+
markerStrokeObj.setWidth(cssPx(el, "--map-marker-stroke-width", 2));
|
|
114
|
+
markerRadiusVal = cssPx(el, "--map-marker-radius", 8);
|
|
115
|
+
polyFillObj.setColor(
|
|
116
|
+
cssVar(el, "--map-polygon-fill", "rgba(255,107,53,0.2)"),
|
|
117
|
+
);
|
|
118
|
+
polyStrokeObj.setColor(cssVar(el, "--map-polygon-stroke", "#ff6b35"));
|
|
119
|
+
polyStrokeObj.setWidth(cssPx(el, "--map-polygon-stroke-width", 2));
|
|
120
|
+
clusterBaseRadiusVal = cssPx(el, "--map-cluster-radius", 16);
|
|
121
|
+
clusterFillColor = cssVar(el, "--map-cluster-fill", "#ff6b35");
|
|
122
|
+
clusterTextColor = cssVar(el, "--map-cluster-text-fill", "#fff");
|
|
123
|
+
const font = cssVar(el, "--map-cluster-font", "monospace");
|
|
124
|
+
const fontSize = cssVar(el, "--map-cluster-font-size", "12px");
|
|
125
|
+
clusterFontStr = `600 ${fontSize} ${font}`;
|
|
126
|
+
|
|
127
|
+
// Rebuild marker image with new radius (CircleStyle radius is immutable)
|
|
128
|
+
marker.setImage(
|
|
129
|
+
new CircleStyle({
|
|
130
|
+
radius: markerRadiusVal,
|
|
131
|
+
fill: markerFillObj,
|
|
132
|
+
stroke: markerStrokeObj,
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Initial read
|
|
138
|
+
readTokens();
|
|
139
|
+
|
|
140
|
+
/** @param {number} size */
|
|
141
|
+
function cluster(size) {
|
|
142
|
+
const key = `c-${size}`;
|
|
143
|
+
let cached = clusterCache.get(key);
|
|
144
|
+
if (cached) return cached;
|
|
145
|
+
|
|
146
|
+
if (size === 1) {
|
|
147
|
+
clusterCache.set(key, marker);
|
|
148
|
+
return marker;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const style = new Style({
|
|
152
|
+
image: new CircleStyle({
|
|
153
|
+
radius: clusterBaseRadiusVal + Math.min(size, 20),
|
|
154
|
+
fill: new Fill({ color: clusterFillColor }),
|
|
155
|
+
}),
|
|
156
|
+
text: new Text({
|
|
157
|
+
text: String(size),
|
|
158
|
+
fill: new Fill({ color: clusterTextColor }),
|
|
159
|
+
font: clusterFontStr,
|
|
160
|
+
}),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
clusterCache.set(key, style);
|
|
164
|
+
return style;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function refresh() {
|
|
168
|
+
readTokens();
|
|
169
|
+
clusterCache.clear(); // cluster styles must be rebuilt with new colors
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { marker, polygon, cluster, refresh };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─── Theme Watcher ───────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Watches for theme changes (data-theme attribute, class changes on <html>,
|
|
179
|
+
* OS prefers-color-scheme). Calls onchange when a theme switch is detected.
|
|
180
|
+
* Returns a dispose function.
|
|
181
|
+
*
|
|
182
|
+
* @param {() => void} onchange
|
|
183
|
+
* @returns {() => void} dispose
|
|
184
|
+
*/
|
|
185
|
+
export function watchTheme(onchange) {
|
|
186
|
+
const mo = new MutationObserver(() => onchange());
|
|
187
|
+
mo.observe(document.documentElement, {
|
|
188
|
+
attributes: true,
|
|
189
|
+
attributeFilter: ["data-theme", "class"],
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const mql = window.matchMedia("(prefers-color-scheme: dark)");
|
|
193
|
+
const handler = () => onchange();
|
|
194
|
+
mql.addEventListener("change", handler);
|
|
195
|
+
|
|
196
|
+
return () => {
|
|
197
|
+
mo.disconnect();
|
|
198
|
+
mql.removeEventListener("change", handler);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── Error Rendering ─────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Renders a visible error state inside a map container.
|
|
206
|
+
*
|
|
207
|
+
* @param {HTMLElement} el
|
|
208
|
+
* @param {string} component
|
|
209
|
+
* @param {Error} error
|
|
210
|
+
*/
|
|
211
|
+
export function renderMapError(el, component, error) {
|
|
212
|
+
const msg = error.message || String(error);
|
|
213
|
+
const isModuleError =
|
|
214
|
+
msg.includes("Failed to fetch") ||
|
|
215
|
+
msg.includes("Module not found") ||
|
|
216
|
+
msg.includes("Cannot find module");
|
|
217
|
+
|
|
218
|
+
el.innerHTML = "";
|
|
219
|
+
const errorEl = document.createElement("div");
|
|
220
|
+
errorEl.setAttribute("role", "alert");
|
|
221
|
+
errorEl.style.cssText = `
|
|
222
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
223
|
+
height: 100%; min-height: 120px; padding: 24px; text-align: center;
|
|
224
|
+
font-family: var(--type-body-sm-font, sans-serif); font-size: var(--type-body-sm-size, 13px);
|
|
225
|
+
color: var(--color-text-muted, #888); background: var(--color-surface-secondary, #f5f5f5);
|
|
226
|
+
border-radius: inherit;
|
|
227
|
+
`;
|
|
228
|
+
|
|
229
|
+
const title = document.createElement("strong");
|
|
230
|
+
title.style.cssText = `
|
|
231
|
+
display: block; margin-bottom: 4px;
|
|
232
|
+
font-family: var(--type-label-font, monospace); font-size: var(--type-label-size, 11px);
|
|
233
|
+
letter-spacing: 0.05em; color: var(--color-destructive, #c00);
|
|
234
|
+
`;
|
|
235
|
+
title.textContent = `${component} FAILED`;
|
|
236
|
+
|
|
237
|
+
const detail = document.createElement("span");
|
|
238
|
+
detail.textContent = isModuleError
|
|
239
|
+
? "OpenLayers (ol) is not installed. Add it to your dependencies."
|
|
240
|
+
: msg;
|
|
241
|
+
|
|
242
|
+
errorEl.appendChild(title);
|
|
243
|
+
errorEl.appendChild(detail);
|
|
244
|
+
el.appendChild(errorEl);
|
|
245
|
+
|
|
246
|
+
console.error(`[${component}]`, error);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ─── CSS Helpers ─────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {Element} el
|
|
253
|
+
* @param {string} prop
|
|
254
|
+
* @param {string} fallback
|
|
255
|
+
* @returns {string}
|
|
256
|
+
*/
|
|
257
|
+
export function cssVar(el, prop, fallback) {
|
|
258
|
+
const val = getComputedStyle(el).getPropertyValue(prop).trim();
|
|
259
|
+
return val || fallback;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Resolves a CSS custom property to a pixel number via probe element.
|
|
264
|
+
*
|
|
265
|
+
* @param {Element} el
|
|
266
|
+
* @param {string} prop
|
|
267
|
+
* @param {number} fallback
|
|
268
|
+
* @returns {number}
|
|
269
|
+
*/
|
|
270
|
+
export function cssPx(el, prop, fallback) {
|
|
271
|
+
const raw = getComputedStyle(el).getPropertyValue(prop).trim();
|
|
272
|
+
if (!raw) return fallback;
|
|
273
|
+
|
|
274
|
+
const probe = document.createElement("div");
|
|
275
|
+
probe.style.cssText = `position:absolute;visibility:hidden;width:${raw}`;
|
|
276
|
+
el.appendChild(probe);
|
|
277
|
+
const px = probe.offsetWidth;
|
|
278
|
+
el.removeChild(probe);
|
|
279
|
+
return px || fallback;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Resolves any CSS color expression (hex, rgb, color-mix, var refs)
|
|
284
|
+
* to a concrete rgb()/rgba() string via a probe element.
|
|
285
|
+
* Required because getComputedStyle doesn't resolve color-mix() on
|
|
286
|
+
* custom properties — only on applied styles like backgroundColor.
|
|
287
|
+
*
|
|
288
|
+
* @param {Element} el
|
|
289
|
+
* @param {string} value — raw CSS color expression
|
|
290
|
+
* @returns {string} — resolved rgb()/rgba() string
|
|
291
|
+
*/
|
|
292
|
+
function resolveColor(el, value) {
|
|
293
|
+
const probe = document.createElement("div");
|
|
294
|
+
probe.style.cssText = `position:absolute;visibility:hidden;background-color:${value}`;
|
|
295
|
+
el.appendChild(probe);
|
|
296
|
+
const resolved = getComputedStyle(probe).backgroundColor;
|
|
297
|
+
el.removeChild(probe);
|
|
298
|
+
return resolved || value;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Extracts RGB components from a resolved color string.
|
|
303
|
+
* Handles rgb(r, g, b) and rgba(r, g, b, a) formats.
|
|
304
|
+
*
|
|
305
|
+
* @param {string} color
|
|
306
|
+
* @returns {{ r: number, g: number, b: number }}
|
|
307
|
+
*/
|
|
308
|
+
function parseRgb(color) {
|
|
309
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
310
|
+
if (match) return { r: +match[1], g: +match[2], b: +match[3] };
|
|
311
|
+
return { r: 254, g: 228, b: 142 };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Reads heatmap gradient stops from CSS custom properties.
|
|
316
|
+
* Resolves color-mix() and other CSS color expressions to concrete
|
|
317
|
+
* rgb values that OL's canvas gradient can consume.
|
|
318
|
+
*
|
|
319
|
+
* @param {Element} el
|
|
320
|
+
* @returns {string[]}
|
|
321
|
+
*/
|
|
322
|
+
export function getHeatmapGradient(el) {
|
|
323
|
+
const styles = getComputedStyle(el);
|
|
324
|
+
const rawStops = [
|
|
325
|
+
styles.getPropertyValue("--map-heatmap-stop-1").trim(),
|
|
326
|
+
styles.getPropertyValue("--map-heatmap-stop-2").trim(),
|
|
327
|
+
styles.getPropertyValue("--map-heatmap-stop-3").trim(),
|
|
328
|
+
styles.getPropertyValue("--map-heatmap-stop-4").trim(),
|
|
329
|
+
].filter(Boolean);
|
|
330
|
+
|
|
331
|
+
if (rawStops.length >= 2) {
|
|
332
|
+
const stops = rawStops.map((s) => resolveColor(el, s));
|
|
333
|
+
const { r, g, b } = parseRgb(stops[0]);
|
|
334
|
+
return [`rgba(${r}, ${g}, ${b}, 0)`, ...stops];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return ["rgba(251, 227, 142, 0)", "#fbe38e", "#fb923c", "#e85a28", "#ae2a1e"];
|
|
338
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiaiai-pt/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Design system tokens and Svelte components for aiaiai products",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
14
|
+
"workspaces": [
|
|
15
|
+
"site"
|
|
16
|
+
],
|
|
14
17
|
"scripts": {
|
|
15
18
|
"build:types": "svelte-package --input . --output dist && cp dist/components/*.d.ts components/ && rm -rf dist",
|
|
16
19
|
"prepublishOnly": "npm run build:types"
|
|
@@ -51,10 +54,14 @@
|
|
|
51
54
|
"@codemirror/state": "^6.6.0",
|
|
52
55
|
"@codemirror/view": "^6.40.0",
|
|
53
56
|
"@lezer/highlight": "^1.0.0",
|
|
57
|
+
"date-fns": "^4.1.0",
|
|
58
|
+
"ol": "^10.0.0",
|
|
54
59
|
"svelte": "^5.0.0"
|
|
55
60
|
},
|
|
56
61
|
"devDependencies": {
|
|
57
62
|
"@sveltejs/package": "^2.5.7",
|
|
63
|
+
"date-fns": "^4.1.0",
|
|
64
|
+
"ol": "^10.8.0",
|
|
58
65
|
"svelte": "^5.55.3",
|
|
59
66
|
"svelte-check": "^4.4.6",
|
|
60
67
|
"typescript": "^5.9.3"
|
package/tokens/components.css
CHANGED
|
@@ -654,6 +654,221 @@
|
|
|
654
654
|
--palette-highlight-color: var(--color-accent);
|
|
655
655
|
--palette-transition: var(--duration-normal) var(--easing-enter);
|
|
656
656
|
|
|
657
|
+
/* ═══════════════════════════════════════════════
|
|
658
|
+
DATEPICKER
|
|
659
|
+
═══════════════════════════════════════════════ */
|
|
660
|
+
|
|
661
|
+
--datepicker-calendar-padding: var(--space-sm);
|
|
662
|
+
|
|
663
|
+
/* Navigation bar */
|
|
664
|
+
--datepicker-nav-font: var(--type-label-font);
|
|
665
|
+
--datepicker-nav-size: var(--type-label-size);
|
|
666
|
+
--datepicker-nav-tracking: var(--type-label-tracking);
|
|
667
|
+
--datepicker-nav-color: var(--color-text);
|
|
668
|
+
--datepicker-nav-btn-size: var(--button-sm-height);
|
|
669
|
+
--datepicker-nav-btn-radius: var(--radius-sm);
|
|
670
|
+
--datepicker-nav-btn-hover-bg: var(--color-surface-secondary);
|
|
671
|
+
|
|
672
|
+
/* Weekday headers */
|
|
673
|
+
--datepicker-weekday-font: var(--type-label-font);
|
|
674
|
+
--datepicker-weekday-size: var(--type-caption-size);
|
|
675
|
+
--datepicker-weekday-tracking: var(--type-caption-tracking);
|
|
676
|
+
--datepicker-weekday-color: var(--color-text-muted);
|
|
677
|
+
|
|
678
|
+
/* Day cells */
|
|
679
|
+
--datepicker-day-size: var(--button-md-height);
|
|
680
|
+
--datepicker-day-font: var(--type-data-font);
|
|
681
|
+
--datepicker-day-font-size: var(--type-body-sm-size);
|
|
682
|
+
--datepicker-day-radius: var(--radius-sm);
|
|
683
|
+
--datepicker-day-hover-bg: var(--color-surface-secondary);
|
|
684
|
+
--datepicker-day-selected-bg: var(--color-accent);
|
|
685
|
+
--datepicker-day-selected-text: var(--color-text-on-accent);
|
|
686
|
+
--datepicker-day-today-border: var(--border-width) solid
|
|
687
|
+
var(--color-border-strong);
|
|
688
|
+
--datepicker-day-outside-opacity: 0.3;
|
|
689
|
+
--datepicker-day-disabled-opacity: 0.2;
|
|
690
|
+
|
|
691
|
+
/* Trigger (inherits --input-* for visual consistency) */
|
|
692
|
+
--datepicker-icon-size: var(--icon-size-sm);
|
|
693
|
+
--datepicker-icon-color: var(--color-text-muted);
|
|
694
|
+
--datepicker-placeholder-color: var(--input-placeholder);
|
|
695
|
+
|
|
696
|
+
/* Range selection (DateRangePicker) */
|
|
697
|
+
--datepicker-range-bg: var(--color-accent-subtle);
|
|
698
|
+
--datepicker-range-hover-bg: var(--color-surface-tertiary);
|
|
699
|
+
--datepicker-range-endpoint-bg: var(--color-accent);
|
|
700
|
+
--datepicker-range-endpoint-text: var(--color-text-on-accent);
|
|
701
|
+
|
|
702
|
+
/* ═══════════════════════════════════════════════
|
|
703
|
+
STAT CARD
|
|
704
|
+
═══════════════════════════════════════════════ */
|
|
705
|
+
|
|
706
|
+
--stat-padding: var(--space-lg);
|
|
707
|
+
--stat-radius: var(--radius-md);
|
|
708
|
+
--stat-border: var(--elevation-border);
|
|
709
|
+
--stat-bg: var(--color-surface);
|
|
710
|
+
|
|
711
|
+
/* Value (large number) */
|
|
712
|
+
--stat-value-font: var(--type-display-font);
|
|
713
|
+
--stat-value-size: var(--type-display-size);
|
|
714
|
+
--stat-value-weight: var(--type-display-weight);
|
|
715
|
+
--stat-value-tracking: var(--type-display-tracking);
|
|
716
|
+
|
|
717
|
+
/* Label */
|
|
718
|
+
--stat-label-font: var(--type-label-font);
|
|
719
|
+
--stat-label-size: var(--type-label-size);
|
|
720
|
+
--stat-label-tracking: var(--type-label-tracking);
|
|
721
|
+
--stat-label-color: var(--color-text-muted);
|
|
722
|
+
|
|
723
|
+
/* Trend */
|
|
724
|
+
--stat-trend-font: var(--type-body-sm-font);
|
|
725
|
+
--stat-trend-size: var(--type-body-sm-size);
|
|
726
|
+
--stat-trend-up-color: var(--color-success);
|
|
727
|
+
--stat-trend-down-color: var(--color-destructive);
|
|
728
|
+
--stat-trend-neutral-color: var(--color-text-muted);
|
|
729
|
+
|
|
730
|
+
/* Icon */
|
|
731
|
+
--stat-icon-size: var(--icon-size-lg);
|
|
732
|
+
--stat-icon-color: var(--color-text-muted);
|
|
733
|
+
|
|
734
|
+
/* Grid */
|
|
735
|
+
--stat-grid-gap: var(--space-md);
|
|
736
|
+
|
|
737
|
+
/* ═══════════════════════════════════════════════
|
|
738
|
+
MAP
|
|
739
|
+
═══════════════════════════════════════════════ */
|
|
740
|
+
|
|
741
|
+
--map-radius: var(--radius-md);
|
|
742
|
+
--map-border: var(--elevation-border);
|
|
743
|
+
|
|
744
|
+
/* Marker */
|
|
745
|
+
--map-marker-radius: var(--space-sm);
|
|
746
|
+
--map-marker-fill: var(--color-accent);
|
|
747
|
+
--map-marker-stroke: var(--color-surface);
|
|
748
|
+
--map-marker-stroke-width: var(--border-width-thick);
|
|
749
|
+
|
|
750
|
+
/* Cluster */
|
|
751
|
+
--map-cluster-radius: var(--space-md);
|
|
752
|
+
--map-cluster-fill: var(--color-accent);
|
|
753
|
+
--map-cluster-text-fill: var(--color-text-on-accent);
|
|
754
|
+
--map-cluster-font: var(--type-label-font);
|
|
755
|
+
--map-cluster-font-size: var(--type-label-size);
|
|
756
|
+
|
|
757
|
+
/* Polygon / draw */
|
|
758
|
+
--map-polygon-fill: color-mix(in srgb, var(--color-accent) 20%, transparent);
|
|
759
|
+
--map-polygon-stroke: var(--color-accent);
|
|
760
|
+
--map-polygon-stroke-width: var(--border-width-thick);
|
|
761
|
+
|
|
762
|
+
/* Heatmap (accent-derived sequential ramp — follows theme) */
|
|
763
|
+
--map-heatmap-stop-1: var(--color-accent-subtle);
|
|
764
|
+
--map-heatmap-stop-2: color-mix(
|
|
765
|
+
in srgb,
|
|
766
|
+
var(--color-accent) 60%,
|
|
767
|
+
var(--color-accent-subtle)
|
|
768
|
+
);
|
|
769
|
+
--map-heatmap-stop-3: var(--color-accent);
|
|
770
|
+
--map-heatmap-stop-4: var(--color-accent-hover);
|
|
771
|
+
|
|
772
|
+
/* Popup (inherits card tokens) */
|
|
773
|
+
--map-popup-bg: var(--card-bg);
|
|
774
|
+
--map-popup-border: var(--elevation-border-strong);
|
|
775
|
+
--map-popup-radius: var(--card-radius);
|
|
776
|
+
--map-popup-padding: var(--space-sm);
|
|
777
|
+
--map-popup-shadow: var(--elevation-overlay);
|
|
778
|
+
--map-popup-arrow-size: var(--space-sm);
|
|
779
|
+
--map-popup-max-width: var(--content-width-narrow);
|
|
780
|
+
|
|
781
|
+
/* Draw mode toolbar */
|
|
782
|
+
--map-toolbar-bg: var(--color-surface);
|
|
783
|
+
--map-toolbar-border: var(--elevation-border);
|
|
784
|
+
--map-toolbar-radius: var(--radius-sm);
|
|
785
|
+
--map-toolbar-padding: var(--space-2xs);
|
|
786
|
+
|
|
787
|
+
/* ═══════════════════════════════════════════════
|
|
788
|
+
CALENDAR
|
|
789
|
+
═══════════════════════════════════════════════ */
|
|
790
|
+
|
|
791
|
+
/* Container */
|
|
792
|
+
--calendar-bg: var(--color-surface);
|
|
793
|
+
--calendar-border: var(--elevation-border);
|
|
794
|
+
--calendar-radius: var(--radius-md);
|
|
795
|
+
--calendar-padding: var(--space-md);
|
|
796
|
+
|
|
797
|
+
/* Toolbar */
|
|
798
|
+
--calendar-toolbar-gap: var(--space-sm);
|
|
799
|
+
--calendar-title-font: var(--type-heading-font);
|
|
800
|
+
--calendar-title-size: var(--type-heading-size);
|
|
801
|
+
--calendar-title-weight: var(--type-heading-weight);
|
|
802
|
+
--calendar-title-tracking: var(--type-heading-tracking);
|
|
803
|
+
--calendar-title-color: var(--color-text);
|
|
804
|
+
|
|
805
|
+
/* View toggle */
|
|
806
|
+
--calendar-toggle-font: var(--type-label-font);
|
|
807
|
+
--calendar-toggle-size: var(--type-label-size);
|
|
808
|
+
--calendar-toggle-tracking: var(--type-label-tracking);
|
|
809
|
+
--calendar-toggle-padding: var(--space-2xs) var(--space-sm);
|
|
810
|
+
--calendar-toggle-radius: var(--radius-sm);
|
|
811
|
+
--calendar-toggle-color: var(--color-text-secondary);
|
|
812
|
+
--calendar-toggle-hover-bg: var(--color-surface-secondary);
|
|
813
|
+
--calendar-toggle-active-bg: var(--color-accent);
|
|
814
|
+
--calendar-toggle-active-text: var(--color-text-on-accent);
|
|
815
|
+
|
|
816
|
+
/* Navigation buttons */
|
|
817
|
+
--calendar-nav-btn-size: var(--button-sm-height);
|
|
818
|
+
--calendar-nav-btn-radius: var(--radius-sm);
|
|
819
|
+
--calendar-nav-btn-hover-bg: var(--color-surface-secondary);
|
|
820
|
+
|
|
821
|
+
/* Weekday headers */
|
|
822
|
+
--calendar-weekday-font: var(--type-label-font);
|
|
823
|
+
--calendar-weekday-size: var(--type-caption-size);
|
|
824
|
+
--calendar-weekday-tracking: var(--type-caption-tracking);
|
|
825
|
+
--calendar-weekday-color: var(--color-text-muted);
|
|
826
|
+
--calendar-weekday-height: var(--space-xl);
|
|
827
|
+
|
|
828
|
+
/* Month cell */
|
|
829
|
+
--calendar-cell-min-height: var(--space-4xl);
|
|
830
|
+
--calendar-cell-border: var(--border-width) solid var(--color-border);
|
|
831
|
+
--calendar-cell-padding: var(--space-2xs);
|
|
832
|
+
--calendar-cell-hover-bg: var(--color-surface-secondary);
|
|
833
|
+
--calendar-cell-today-bg: var(--color-accent-subtle);
|
|
834
|
+
|
|
835
|
+
/* Day number */
|
|
836
|
+
--calendar-day-font: var(--type-data-font);
|
|
837
|
+
--calendar-day-size: var(--type-body-sm-size);
|
|
838
|
+
--calendar-day-color: var(--color-text);
|
|
839
|
+
--calendar-day-outside-opacity: 0.3;
|
|
840
|
+
--calendar-day-today-size: var(--space-lg);
|
|
841
|
+
--calendar-day-today-bg: var(--color-accent);
|
|
842
|
+
--calendar-day-today-text: var(--color-text-on-accent);
|
|
843
|
+
|
|
844
|
+
/* Time gutter (week/day views) */
|
|
845
|
+
--calendar-time-font: var(--type-data-font);
|
|
846
|
+
--calendar-time-size: var(--type-caption-size);
|
|
847
|
+
--calendar-time-color: var(--color-text-muted);
|
|
848
|
+
--calendar-time-width: var(--space-3xl);
|
|
849
|
+
|
|
850
|
+
/* Hour slot (week/day views) */
|
|
851
|
+
--calendar-slot-height: var(--space-2xl);
|
|
852
|
+
--calendar-slot-border: var(--border-width) solid var(--color-border);
|
|
853
|
+
|
|
854
|
+
/* Now indicator */
|
|
855
|
+
--calendar-now-color: var(--color-destructive);
|
|
856
|
+
--calendar-now-width: var(--border-width-thick);
|
|
857
|
+
|
|
858
|
+
/* Event block */
|
|
859
|
+
--calendar-event-font: var(--type-body-sm-font);
|
|
860
|
+
--calendar-event-size: var(--type-caption-size);
|
|
861
|
+
--calendar-event-weight: var(--type-body-sm-weight);
|
|
862
|
+
--calendar-event-radius: var(--radius-sm);
|
|
863
|
+
--calendar-event-padding: var(--space-2xs) var(--space-xs);
|
|
864
|
+
--calendar-event-default-bg: var(--color-accent);
|
|
865
|
+
--calendar-event-default-text: var(--color-text-on-accent);
|
|
866
|
+
|
|
867
|
+
/* Overflow indicator */
|
|
868
|
+
--calendar-overflow-font: var(--type-caption-font);
|
|
869
|
+
--calendar-overflow-size: var(--type-caption-size);
|
|
870
|
+
--calendar-overflow-color: var(--color-text-muted);
|
|
871
|
+
|
|
657
872
|
/* ═══════════════════════════════════════════════
|
|
658
873
|
FOCUS RING (global)
|
|
659
874
|
═══════════════════════════════════════════════ */
|