@granularjs/ui 0.3.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@granularjs/ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "90+ production-ready UI components for Granular. Dark/light themes, CSS variables, accessible, zero dependencies beyond @granularjs/core.",
5
5
  "type": "module",
6
6
  "main": "./dist/granular-ui.min.js",
@@ -17,11 +17,6 @@ export function List(...args) {
17
17
  value && typeof value === 'object' && typeof value.tagName === 'string' &&
18
18
  value.tagName.toLowerCase() === 'li';
19
19
  const wrapChild = (child) => {
20
- console.log('INFO ABOUT ITEM', child,
21
- typeof child,
22
- typeof child?.tagName,
23
- child?.tagName?.toLowerCase()
24
- );
25
20
  const wrapValue = (value) => {
26
21
  if (value?.nodeType === 'granular-list-node') return value;
27
22
  if (value == null || value === false) return null;
@@ -55,12 +50,17 @@ export function List(...args) {
55
50
 
56
51
  export function ListItem(...args) {
57
52
  const { props, children } = splitPropsChildren(args, { withBorder: false });
58
- const { leftSection, rightSection, title, body, withBorder, className, ...rest } = props;
53
+ const { leftSection, rightSection, title, body, withBorder, className, verticalPadding, horizontalPadding, ...rest } = props;
59
54
  const hasStructured = after(title, body).compute(([nextTitle, nextBody]) => !!nextTitle || !!nextBody);
60
55
  return Li(
61
56
  {
62
57
  ...rest,
63
- className: cx('g-ui-list-item', classFlag('g-ui-list-item-border', withBorder), className),
58
+ className: cx('g-ui-list-item',
59
+ classFlag('g-ui-list-item-border', withBorder),
60
+ classVar('g-ui-list-item-vertical-padding-', verticalPadding, 'md'),
61
+ classVar('g-ui-list-item-horizontal-padding-', horizontalPadding, 'md'),
62
+ className
63
+ ),
64
64
  },
65
65
  Div(
66
66
  { className: 'g-ui-list-item-shell' },
@@ -46,7 +46,6 @@ const TableRow = (row, header) => {
46
46
 
47
47
  const ArrayRow = (row) => {
48
48
  return list(row, (next) => {
49
- console.log('INFO ABOUT NEXT', header);
50
49
  return header ? TableHeaderCell(next) : TableCell(next)
51
50
  })
52
51
  }
@@ -1,7 +1,17 @@
1
- import { Div, when, list, after, resolve, state } from '@granularjs/core';
2
- import { cx, splitPropsChildren } from '../utils.js';
1
+ import { Div, when, list, after, resolve, state, Img, Span } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, classVar } from '../utils.js';
3
3
 
4
- const DOT_CENTER_OFFSET_PX = 10;
4
+ const PIN_CENTER_OFFSET = { xs: 6, sm: 8, md: 10, lg: 12, xl: 14 };
5
+ const LINE_WIDTH_CSS = { xs: '2px', sm: '3px', md: '4px', lg: '6px', xl: '8px' };
6
+ const PIN_HALF_CSS = { xs: '6px', sm: '8px', md: '10px', lg: '12px', xl: '14px' };
7
+ const PIN_COLUMN_CENTER_PX = 14;
8
+
9
+ function resolveActiveColor(color) {
10
+ if (color == null || color === '') return 'var(--g-ui-primary)';
11
+ const s = String(color).trim();
12
+ if (s.startsWith('#')) return s;
13
+ return `var(--g-ui-${s})`;
14
+ }
5
15
 
6
16
  function getWeights(items) {
7
17
  const list = items ?? [];
@@ -106,12 +116,14 @@ let timelineIdCounter = 0;
106
116
 
107
117
  function measureSegmentLayout(timelineId) {
108
118
  const el = document.getElementById(timelineId);
109
- if (!el) return;
119
+ if (!el) return [];
110
120
  const itemEls = el.querySelectorAll('.g-ui-timeline-item');
111
121
  if (itemEls.length < 2) return [];
122
+ const pinSize = el.dataset.pinSize || 'md';
123
+ const offset = PIN_CENTER_OFFSET[pinSize] ?? PIN_CENTER_OFFSET.md;
112
124
  const segments = [];
113
125
  for (let i = 0; i < itemEls.length - 1; i++) {
114
- const top = itemEls[i].offsetTop + DOT_CENTER_OFFSET_PX;
126
+ const top = itemEls[i].offsetTop + offset;
115
127
  const height = itemEls[i + 1].offsetTop - itemEls[i].offsetTop;
116
128
  segments.push({ top, height });
117
129
  }
@@ -128,6 +140,13 @@ export function Timeline(...args) {
128
140
  stepDurationsMs: null,
129
141
  totalDurationMs: null,
130
142
  clickable: false,
143
+ pinRadius: 'md',
144
+ reverseActive: false,
145
+ lineWidth: 'md',
146
+ pinSize: 'md',
147
+ activeColor: 'primary',
148
+ align: 'left',
149
+ pinMode: 'default',
131
150
  });
132
151
  const {
133
152
  items,
@@ -138,14 +157,22 @@ export function Timeline(...args) {
138
157
  stepDurationsMs,
139
158
  totalDurationMs,
140
159
  clickable,
160
+ pinRadius,
161
+ reverseActive,
162
+ lineWidth,
163
+ pinSize,
164
+ activeColor,
165
+ align,
166
+ pinMode,
141
167
  className,
142
168
  ...rest
143
169
  } = props;
144
170
  const { onChange } = rawProps;
145
171
 
172
+ const activeColorResolved = after(activeColor).compute((c) => resolveActiveColor(resolve(c)));
173
+
146
174
  const timelineId = `g-ui-timeline-${++timelineIdCounter}`;
147
175
  const segmentLayout = state([]);
148
-
149
176
  const state_ = after(mode, active, progress, elapsedMs, stepDurationsMs, totalDurationMs, items).compute(
150
177
  (values) => {
151
178
  const [mode, active, progress, elapsedMs, stepDurationsMs, totalDurationMs, items] = values;
@@ -160,6 +187,19 @@ export function Timeline(...args) {
160
187
  );
161
188
  }
162
189
  );
190
+ const reverseTrackLayout = after(segmentLayout, reverseActive).compute(([segs, rev]) => {
191
+ if (!resolve(rev) || !segs?.length) return null;
192
+ const first = segs[0];
193
+ let totalHeight = 0;
194
+ for (const s of segs) totalHeight += s.height;
195
+ return { top: first.top, height: totalHeight };
196
+ });
197
+
198
+ const reverseFillHeight = after(state_).compute((s) =>
199
+ s?.progressPct != null ? `${Math.max(0, Math.min(100, s.progressPct))}%` : '0%'
200
+ );
201
+
202
+
163
203
 
164
204
  const showTrack = after(mode).compute((m) => {
165
205
  const v = resolve(m);
@@ -176,83 +216,162 @@ export function Timeline(...args) {
176
216
  }
177
217
 
178
218
  after(items).change(() => scheduleMeasure());
219
+ after(pinSize).change(() => scheduleMeasure());
179
220
  scheduleMeasure();
180
221
 
181
222
  return Div(
182
223
  {
183
224
  ...rest,
184
225
  id: timelineId,
226
+ 'data-pin-size': after(pinSize).compute((s) => resolve(s) ?? 'md'),
227
+ 'data-active-color': after(activeColor).compute((a) => {
228
+ const v = resolve(a);
229
+ if (v == null || typeof v !== 'string') return 'primary';
230
+ const s = String(v).trim();
231
+ if (s.startsWith('#')) return 'custom';
232
+ return s || 'primary';
233
+ }),
234
+ style: after(activeColor, lineWidth, pinSize).compute(([a, lw, ps]) => {
235
+ const res = {
236
+ '--g-ui-timeline-line-width': LINE_WIDTH_CSS[resolve(lw)] ?? '4px',
237
+ '--g-ui-timeline-track-offset': `calc(${PIN_COLUMN_CENTER_PX}px - var(--g-ui-timeline-line-width) / 2)`,
238
+ '--g-ui-timeline-pin-half': PIN_HALF_CSS[resolve(ps)] ?? '10px',
239
+ };
240
+ const colorVal = resolve(a);
241
+ if (colorVal && String(colorVal).trim().startsWith('#'))
242
+ res['--g-ui-timeline-active-color'] = String(colorVal).trim();
243
+ return res;
244
+ }),
185
245
  className: cx(
186
246
  'g-ui-timeline',
187
247
  after(mode).compute((m) => (m ? `g-ui-timeline-mode-${resolve(m)}` : '')),
188
248
  after(showTrack).compute((show) => (show ? 'g-ui-timeline-has-track' : '')),
189
249
  after(clickable).compute((c) => (resolve(c) ? 'g-ui-timeline-clickable' : '')),
250
+ after(reverseActive).compute((r) => (resolve(r) ? 'g-ui-timeline-reverse' : '')),
251
+ after(align).compute((a) => (resolve(a) === 'right' ? 'g-ui-timeline-align-right' : '')),
252
+ classVar('g-ui-timeline-pin-radius-', pinRadius, 'md'),
253
+ classVar('g-ui-timeline-line-width-', lineWidth, 'md'),
254
+ classVar('g-ui-timeline-pin-size-', pinSize, 'md'),
255
+ after(pinMode).compute((p) => (p ? `g-ui-timeline-pin-mode-${resolve(p)}` : '')),
190
256
  className
191
257
  ),
192
258
  },
193
259
  when(showTrack, () =>
194
- list(segmentLayout, (seg, idx) => {
195
- const segStyle = after(seg).compute((s) =>
196
- s ? { top: `${s.top}px`, height: `${s.height}px` } : {}
197
- );
198
- const fillPct = after(state_, idx, items, resolvedMode).compute(([s, i, its, m]) => {
199
- const itsList = its ?? [];
200
- const n = itsList.length;
201
- const weightContext = getWeightContext(itsList);
202
- return computeSegmentFillPercent(
203
- m,
204
- s?.activeStep ?? 0,
205
- s?.progressPct ?? 0,
206
- resolve(i) ?? 0,
207
- n,
208
- weightContext
260
+ after(reverseActive).compute((rev) => {
261
+ if (resolve(rev)) {
262
+ return Div(
263
+ {
264
+ className: 'g-ui-timeline-track-segment g-ui-timeline-track-reverse',
265
+ style: after(reverseTrackLayout).compute((l) =>
266
+ l ? { top: `${l.top}px`, height: `${l.height}px` } : {}
267
+ ),
268
+ },
269
+ Div({
270
+ className: 'g-ui-timeline-track-fill',
271
+ style: after(reverseFillHeight).compute((h) => (h ? { height: h } : { height: '0%' })),
272
+ })
273
+ );
274
+ }
275
+ return list(segmentLayout, (seg, idx) => {
276
+ const segStyle = after(seg).compute((s) =>
277
+ s ? { top: `${s.top}px`, height: `${s.height}px` } : {}
278
+ );
279
+ const fillPct = after(state_, idx, items, resolvedMode).compute(([s, i, its, m]) => {
280
+ const itsList = its ?? [];
281
+ const n = itsList.length;
282
+ const weightContext = getWeightContext(itsList);
283
+ return computeSegmentFillPercent(
284
+ m,
285
+ s?.activeStep ?? 0,
286
+ s?.progressPct ?? 0,
287
+ resolve(i) ?? 0,
288
+ n,
289
+ weightContext
290
+ );
291
+ });
292
+ const fillHeight = after(fillPct).compute((p) => `${Math.max(0, Math.min(100, p))}%`);
293
+ return Div(
294
+ {
295
+ className: 'g-ui-timeline-track-segment',
296
+ style: after(segStyle).compute((x) => x),
297
+ },
298
+ Div({
299
+ className: 'g-ui-timeline-track-fill',
300
+ style: after(fillHeight).compute((h) => (h ? { height: h } : { height: '0%' })),
301
+ })
209
302
  );
210
303
  });
211
- const fillHeight = after(fillPct).compute((p) => `${Math.max(0, Math.min(100, p))}%`);
212
- return Div(
213
- {
214
- className: 'g-ui-timeline-track-segment',
215
- style: after(segStyle).compute((x) => x),
216
- },
217
- Div({
218
- className: 'g-ui-timeline-track-fill',
219
- style: after(fillHeight).compute((h) => (h ? { height: h } : { height: '0%' })),
220
- })
221
- );
222
304
  })
223
305
  ),
224
306
  list(items, (item, idx) => {
225
- const itemState = after(state_, idx).compute(([s, i]) => {
307
+ const itemState = after(state_, idx, items, reverseActive).compute(([s, i, its, rev]) => {
226
308
  const step = s?.activeStep ?? 0;
227
309
  const index = resolve(i) ?? 0;
228
- if (index < step) return 'completed';
229
- if (index === step) return 'active';
310
+ const n = (its ?? []).length;
311
+ const logicalIndex = resolve(rev) ? n - 1 - index : index;
312
+ if (logicalIndex < step) return 'completed';
313
+ if (logicalIndex === step) return 'active';
230
314
  return 'future';
231
315
  });
232
316
  const itemClass = after(itemState).compute((st) => (st ? `g-ui-timeline-item-${st}` : ''));
233
317
  const handleClick =
234
318
  resolve(clickable) && typeof onChange === 'function'
235
319
  ? () => {
236
- const i = resolve(idx);
237
- if (typeof i === 'number') onChange(i);
238
- }
320
+ const i = resolve(idx);
321
+ if (typeof i === 'number') onChange(i);
322
+ }
239
323
  : undefined;
324
+ const pinModeVal = after(pinMode).compute((p) => resolve(p) ?? 'default');
325
+ const pinExtra = after(pinModeVal, item).compute(([mode, it]) => {
326
+ const m = mode ?? 'default';
327
+ if (m === 'icon' && (it?.icon != null || it?.pinIcon != null))
328
+ return Span(
329
+ { className: 'g-ui-timeline-pin-icon material-symbols-outlined' },
330
+ it.icon ?? it.pinIcon ?? ''
331
+ );
332
+ if (m === 'image' && (it?.image != null || it?.pinImage != null || it?.src != null))
333
+ return Img({
334
+ className: 'g-ui-timeline-pin-image',
335
+ src: it.image ?? it.pinImage ?? it.src,
336
+ alt: it.pinImageAlt ?? '',
337
+ });
338
+ if (m === 'custom' && it?.pinContent != null) return it.pinContent;
339
+ return null;
340
+ });
341
+ const hasPinExtra = after(pinModeVal, item).compute(([mode, it]) => {
342
+ const m = mode ?? 'default';
343
+ if (m === 'icon') return it?.icon != null || it?.pinIcon != null;
344
+ if (m === 'image') return it?.image != null || it?.pinImage != null || it?.src != null;
345
+ if (m === 'custom') return it?.pinContent != null;
346
+ return false;
347
+ });
348
+ const dotBlock = Div(
349
+ { className: 'g-ui-timeline-dot' },
350
+ Div({ className: 'g-ui-timeline-dot-inner' }),
351
+ when(hasPinExtra, () => pinExtra)
352
+ );
353
+ const contentBlock = Div(
354
+ { className: 'g-ui-timeline-content' },
355
+ when(item.title, () => Div({ className: 'g-ui-timeline-title' }, item.title)),
356
+ when(item.description, () =>
357
+ Div({ className: 'g-ui-timeline-desc' }, item.description)
358
+ ),
359
+ item.content
360
+ );
240
361
  return Div(
241
362
  {
242
363
  className: cx('g-ui-timeline-item', itemClass),
364
+ style: after(activeColorResolved).compute((c) =>
365
+ c ? { '--g-ui-timeline-active-color': c } : undefined
366
+ ),
243
367
  onClick: handleClick,
244
368
  role: handleClick ? 'button' : undefined,
245
369
  tabIndex: handleClick ? 0 : undefined,
246
370
  },
247
- Div({ className: 'g-ui-timeline-dot' }),
248
- Div(
249
- { className: 'g-ui-timeline-content' },
250
- when(item.title, () => Div({ className: 'g-ui-timeline-title' }, item.title)),
251
- when(item.description, () =>
252
- Div({ className: 'g-ui-timeline-desc' }, item.description)
253
- ),
254
- item.content
255
- )
371
+ Div({ className: 'g-ui-dot-wrapper' },
372
+ dotBlock,
373
+ ),
374
+ contentBlock
256
375
  );
257
376
  })
258
377
  );
@@ -1812,6 +1812,8 @@ body .g-ui-card-shadow-lg { box-shadow: var(--g-ui-shadow-lg); }
1812
1812
  align-self: flex-start;
1813
1813
  justify-self: end;
1814
1814
  color: currentColor;
1815
+ position: relative;
1816
+ left: 3px;
1815
1817
  }
1816
1818
  .g-ui-list-unordered > li:not(.g-ui-list-nested-item)::before {
1817
1819
  content: '•';
@@ -2134,10 +2136,16 @@ body .g-ui-card-shadow-lg { box-shadow: var(--g-ui-shadow-lg); }
2134
2136
  gap: 0;
2135
2137
  position: relative;
2136
2138
  }
2139
+ .g-ui-timeline[data-active-color="primary"] { --g-ui-timeline-active-color: var(--g-ui-primary); }
2140
+ .g-ui-timeline[data-active-color="success"] { --g-ui-timeline-active-color: var(--g-ui-success); }
2141
+ .g-ui-timeline[data-active-color="danger"] { --g-ui-timeline-active-color: var(--g-ui-danger); }
2142
+ .g-ui-timeline[data-active-color="error"] { --g-ui-timeline-active-color: var(--g-ui-danger); }
2143
+ .g-ui-timeline[data-active-color="warning"] { --g-ui-timeline-active-color: var(--g-ui-warning); }
2144
+ .g-ui-timeline[data-active-color="info"] { --g-ui-timeline-active-color: var(--g-ui-info); }
2137
2145
  .g-ui-timeline-track-segment {
2138
2146
  position: absolute;
2139
- left: 8px;
2140
- width: 4px;
2147
+ left: var(--g-ui-timeline-track-offset, 12px);
2148
+ width: var(--g-ui-timeline-line-width, 4px);
2141
2149
  background: var(--g-ui-border-muted);
2142
2150
  border-radius: 2px;
2143
2151
  z-index: 0;
@@ -2151,11 +2159,19 @@ body .g-ui-card-shadow-lg { box-shadow: var(--g-ui-shadow-lg); }
2151
2159
  width: 100%;
2152
2160
  height: 0%;
2153
2161
  min-height: 0;
2154
- background: var(--g-ui-primary);
2155
- border-radius: 2px;
2162
+ background: var(--g-ui-timeline-active-color, var(--g-ui-primary));
2163
+ border-radius: inherit;
2156
2164
  transition: height 0.25s ease;
2157
2165
  z-index: 1;
2158
2166
  }
2167
+ .g-ui-timeline-reverse .g-ui-timeline-track-fill {
2168
+ top: auto;
2169
+ bottom: 0;
2170
+ }
2171
+ .g-ui-timeline-align-right .g-ui-timeline-track-segment {
2172
+ left: auto;
2173
+ right: var(--g-ui-timeline-track-offset, 12px);
2174
+ }
2159
2175
  .g-ui-timeline-has-track .g-ui-timeline-item::before {
2160
2176
  display: none;
2161
2177
  }
@@ -2168,20 +2184,48 @@ body .g-ui-card-shadow-lg { box-shadow: var(--g-ui-shadow-lg); }
2168
2184
  padding-bottom: var(--g-ui-space-24);
2169
2185
  z-index: 1;
2170
2186
  }
2187
+ .g-ui-timeline-item .g-ui-timeline-dot {
2188
+ justify-self: center;
2189
+ }
2190
+ .g-ui-timeline-align-right {
2191
+ width: max-content;
2192
+ max-width: 100%;
2193
+ }
2194
+ .g-ui-timeline-align-right .g-ui-timeline-item {
2195
+ grid-template-columns: auto 28px;
2196
+ max-width: 100%;
2197
+ }
2198
+ .g-ui-timeline-align-right .g-ui-timeline-item .g-ui-timeline-dot {
2199
+ order: 2;
2200
+ justify-self: center;
2201
+ }
2202
+ .g-ui-timeline-align-right .g-ui-timeline-item .g-ui-timeline-content {
2203
+ order: 1;
2204
+ justify-self: end;
2205
+ text-align: right;
2206
+ min-width: 0;
2207
+ }
2208
+ .g-ui-timeline-align-right .g-ui-timeline-item::before {
2209
+ left: auto;
2210
+ right: var(--g-ui-timeline-track-offset, 12px);
2211
+ }
2171
2212
  .g-ui-timeline-item:last-child {
2172
2213
  padding-bottom: 0;
2173
2214
  }
2174
2215
  .g-ui-timeline-item::before {
2175
2216
  content: '';
2176
2217
  position: absolute;
2177
- left: 8px;
2178
- top: 20px;
2218
+ left: var(--g-ui-timeline-track-offset, 12px);
2219
+ top: var(--g-ui-timeline-pin-half, 10px);
2179
2220
  bottom: 0;
2180
- width: 4px;
2221
+ width: var(--g-ui-timeline-line-width, 4px);
2181
2222
  background: var(--g-ui-border-muted);
2182
2223
  }
2183
2224
  .g-ui-timeline-item:last-child::before {
2184
2225
  display: none;
2226
+ }
2227
+ .g-ui-dot-wrapper {
2228
+
2185
2229
  }
2186
2230
  .g-ui-timeline-dot {
2187
2231
  width: 20px;
@@ -2193,15 +2237,68 @@ body .g-ui-card-shadow-lg { box-shadow: var(--g-ui-shadow-lg); }
2193
2237
  position: relative;
2194
2238
  z-index: 1;
2195
2239
  transition: border-color 0.2s ease, background 0.2s ease;
2240
+ display: flex;
2241
+ align-items: center;
2242
+ justify-content: center;
2243
+ }
2244
+ .g-ui-timeline-pin-size-xs .g-ui-timeline-dot { width: 12px; height: 12px; border-width: 2px; }
2245
+ .g-ui-timeline-pin-size-sm .g-ui-timeline-dot { width: 16px; height: 16px; border-width: 3px; }
2246
+ .g-ui-timeline-pin-size-md .g-ui-timeline-dot { width: 20px; height: 20px; border-width: 4px; }
2247
+ .g-ui-timeline-pin-size-lg .g-ui-timeline-dot { width: 24px; height: 24px; border-width: 4px; }
2248
+ .g-ui-timeline-pin-size-xl .g-ui-timeline-dot { width: 28px; height: 28px; border-width: 4px; }
2249
+ .g-ui-timeline-pin-radius-xs .g-ui-timeline-dot { border-radius: 2px; }
2250
+ .g-ui-timeline-pin-radius-sm .g-ui-timeline-dot { border-radius: 4px; }
2251
+ .g-ui-timeline-pin-radius-md .g-ui-timeline-dot { border-radius: 50%; }
2252
+ .g-ui-timeline-pin-radius-lg .g-ui-timeline-dot { border-radius: 50%; }
2253
+ .g-ui-timeline-pin-radius-xl .g-ui-timeline-dot { border-radius: 50%; }
2254
+ .g-ui-timeline-dot-inner {
2255
+ width: 100%;
2256
+ height: 100%;
2257
+ border-radius: inherit;
2258
+ background: inherit;
2259
+ position: absolute;
2260
+ inset: 0;
2261
+ }
2262
+ .g-ui-timeline-pin-mode-icon .g-ui-timeline-dot,
2263
+ .g-ui-timeline-pin-mode-image .g-ui-timeline-dot,
2264
+ .g-ui-timeline-pin-mode-custom .g-ui-timeline-dot {
2265
+ display: flex;
2266
+ align-items: center;
2267
+ justify-content: center;
2268
+ }
2269
+ .g-ui-timeline-pin-mode-icon .g-ui-timeline-dot .g-ui-timeline-pin-icon,
2270
+ .g-ui-timeline-pin-mode-image .g-ui-timeline-dot .g-ui-timeline-pin-image,
2271
+ .g-ui-timeline-pin-mode-custom .g-ui-timeline-dot > *:not(.g-ui-timeline-dot-inner) {
2272
+ position: relative;
2273
+ z-index: 1;
2274
+ }
2275
+ .g-ui-timeline-pin-mode-icon .g-ui-timeline-dot .g-ui-timeline-pin-icon {
2276
+ font-size: 12px;
2277
+ line-height: 1;
2278
+ color: inherit;
2279
+ }
2280
+ .g-ui-timeline-pin-size-sm .g-ui-timeline-pin-mode-icon .g-ui-timeline-dot .g-ui-timeline-pin-icon { font-size: 10px; }
2281
+ .g-ui-timeline-pin-size-lg .g-ui-timeline-dot .g-ui-timeline-pin-icon { font-size: 14px; }
2282
+ .g-ui-timeline-pin-size-xl .g-ui-timeline-dot .g-ui-timeline-pin-icon { font-size: 16px; }
2283
+ .g-ui-timeline-pin-mode-icon .g-ui-timeline-dot .g-ui-timeline-dot-inner,
2284
+ .g-ui-timeline-pin-mode-image .g-ui-timeline-dot .g-ui-timeline-dot-inner,
2285
+ .g-ui-timeline-pin-mode-custom .g-ui-timeline-dot .g-ui-timeline-dot-inner {
2286
+ display: none;
2287
+ }
2288
+ .g-ui-timeline-pin-mode-image .g-ui-timeline-dot .g-ui-timeline-pin-image {
2289
+ width: 100%;
2290
+ height: 100%;
2291
+ object-fit: cover;
2292
+ border-radius: inherit;
2196
2293
  }
2197
2294
  .g-ui-timeline-item-completed .g-ui-timeline-dot {
2198
- border-color: var(--g-ui-primary);
2199
- background: var(--g-ui-primary);
2295
+ border-color: var(--g-ui-timeline-active-color, var(--g-ui-primary));
2296
+ background: var(--g-ui-timeline-active-color, var(--g-ui-primary));
2200
2297
  }
2201
2298
  .g-ui-timeline-item-active .g-ui-timeline-dot {
2202
- border-color: var(--g-ui-primary);
2299
+ border-color: var(--g-ui-timeline-active-color, var(--g-ui-primary));
2203
2300
  background: var(--g-ui-bg);
2204
- box-shadow: 0 0 0 2px var(--g-ui-bg), 0 0 0 4px var(--g-ui-primary);
2301
+ box-shadow: 0 0 0 2px var(--g-ui-bg), 0 0 0 4px var(--g-ui-timeline-active-color, var(--g-ui-primary));
2205
2302
  }
2206
2303
  .g-ui-timeline-item-future .g-ui-timeline-dot {
2207
2304
  border-color: var(--g-ui-border-muted);
@@ -2211,7 +2308,7 @@ body .g-ui-card-shadow-lg { box-shadow: var(--g-ui-shadow-lg); }
2211
2308
  cursor: pointer;
2212
2309
  }
2213
2310
  .g-ui-timeline-clickable .g-ui-timeline-item:hover .g-ui-timeline-dot {
2214
- border-color: var(--g-ui-primary-muted, var(--g-ui-primary));
2311
+ border-color: var(--g-ui-primary-muted, var(--g-ui-timeline-active-color));
2215
2312
  }
2216
2313
  .g-ui-timeline-content {
2217
2314
  display: flex;