@cahyo-dimas/freeday 1.11.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,43 @@
3
3
  Semua perubahan penting dicatat di sini. Format longgar mengikuti
4
4
  [Keep a Changelog](https://keepachangelog.com/); tiap versi = git tag.
5
5
 
6
+ ## [1.13.0] — 2026-07-30
7
+ ### Added
8
+ - **`FdyDatepicker` clear affordance — an optional date can now be unset (#37A).** A new `clearable`
9
+ prop renders a small × button in the trigger (overlaid in the calendar-icon slot) whenever a date is
10
+ set, emitting `''` (`update:modelValue` + `change` in Vue, `onChange('')` in React) to return to
11
+ empty — closing a functional regression vs the native `<input type="date">`, which offered a clear
12
+ control. Off by default; both adapters. Verified in-browser: the × is right-aligned in the icon slot,
13
+ vertically centred, hides the calendar icon while shown, and never overlaps the value text (even a
14
+ long, ellipsised one).
15
+ - **`FdyDatepicker` month-nav labels are now overridable (#37B).** The previous/next-month `aria-label`s
16
+ were hardcoded, leaking to every screen-reader user with no way to change them; added
17
+ `prevMonthLabel` / `nextMonthLabel` props (plus `clearLabel` for the new × button). Month and weekday
18
+ names already localise via `Intl` + the `locale` prop.
19
+ ### Changed
20
+ - **`FdyDatepicker` default UI strings are now English** (`Select date` placeholder, `Previous month` /
21
+ `Next month` nav labels) instead of Indonesian, matching the kit's English-first public docs. Pass
22
+ `placeholder` / `prevMonthLabel` / `nextMonthLabel` for other languages. Scope is the Vue + React
23
+ `FdyDatepicker` adapters only — the vanilla `freeday-datepicker.js` enhancer (used by the Indonesian
24
+ docs demos) keeps its Indonesian defaults.
25
+
26
+ ## [1.12.0] — 2026-07-29
27
+ ### Added
28
+ - **Chart colour overrides can now name a categorical palette slot (#36).** `data-fdy-colors` (and the
29
+ `<FdyChart colors>` prop) previously resolved every name through the semantic tier
30
+ (`var(--color-<name>)`), so an override could only reach `primary`/`info`/`accent`/… — never the
31
+ validated categorical palette `--chart-1..8`. An app that needs stable *per-category* colours (rather
32
+ than per-array-index) had to override, and overriding then dropped each series onto the nearest
33
+ semantic hue — e.g. a three-way split landing on three neighbouring blues: contrast-passing but
34
+ unreadable as a comparison. `colorVar` now maps a name of the form `chart-1`..`chart-8` to
35
+ `var(--chart-N)`, so an override can pin a series to the validated palette
36
+ (`colors={['chart-1','chart-2','chart-3']}`). Backward compatible — no semantic token name starts
37
+ with `chart-`, so semantic overrides are unchanged; the single-series `data-fdy-color` / `color`
38
+ accepts the `chart-N` form too. Verified in-browser: explicit `chart-1..3` now render the exact
39
+ fixed-order palette (blue/orange/green) and equal the default, while `success,warning,danger` still
40
+ resolve to their semantic tokens. (`--chart-1..8` are public tokens in `dist/freeday.tokens.css`, so
41
+ matching swatches/bars elsewhere can reference `var(--chart-N)` directly — no adapter export needed.)
42
+
6
43
  ## [1.11.2] — 2026-07-28
7
44
  ### Fixed
8
45
  - **A filter bar docked in a table toolbar no longer loses its alignment (#35).** `.fdy-filterbar`
package/README.id.md CHANGED
@@ -5,7 +5,7 @@
5
5
  > **Lebih banyak _free day_ buat dev — UI kit-nya sudah siap pakai.**
6
6
 
7
7
  [![Live docs](https://img.shields.io/badge/docs-live-2050d8?style=flat-square)](https://cahyo-dimas.github.io/freeday-ui-kit/)
8
- [![Release](https://img.shields.io/badge/release-v1.11.2-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v1.11.2)
8
+ [![Release](https://img.shields.io/badge/release-v1.13.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v1.13.0)
9
9
 
10
10
  UI KIT yang token-driven & framework-agnostic — satu sumber kebenaran untuk warna, tipografi,
11
11
  spasi, dan komponen. Blueprint: `docs/superpowers/specs/2026-07-21-freeday-ui-kit-design.md`.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  > **More free days for devs — the UI kit is ready to use.**
6
6
 
7
7
  [![Live docs](https://img.shields.io/badge/docs-live-2050d8?style=flat-square)](https://cahyo-dimas.github.io/freeday-ui-kit/)
8
- [![Release](https://img.shields.io/badge/release-v1.11.2-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v1.11.2)
8
+ [![Release](https://img.shields.io/badge/release-v1.13.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v1.13.0)
9
9
 
10
10
  A token-driven, framework-agnostic UI kit — one source of truth for color, typography,
11
11
  spacing, and components. Blueprint: `docs/superpowers/specs/2026-07-21-freeday-ui-kit-design.md`.
@@ -14,6 +14,11 @@ export interface FdyChartProps {
14
14
  format?: 'number' | 'percent' | 'currency';
15
15
  stacked?: boolean;
16
16
  legend?: 'auto' | 'always' | 'none';
17
+ /**
18
+ * Per-series colour override. Each entry is a semantic token name (primary/accent/success/
19
+ * warning/danger/info) OR a categorical slot 'chart-1'..'chart-8' to pin a series to the
20
+ * validated palette. Omit to use the default fixed-order chart palette.
21
+ */
17
22
  colors?: ReadonlyArray<string>;
18
23
  color?: string;
19
24
  center?: string | number;
@@ -33,6 +33,14 @@ export interface FdyDatepickerProps {
33
33
  describedby?: string;
34
34
  id?: string;
35
35
  ariaLabelledby?: string;
36
+ /** Show a clear (×) button in the trigger when a date is set, so an optional date can be unset. Calls onChange('') to reset. Off by default. */
37
+ clearable?: boolean;
38
+ /** aria-label for the previous-month nav button. Default 'Previous month' — override for non-English UIs (month/weekday names already follow `locale`). */
39
+ prevMonthLabel?: string;
40
+ /** aria-label for the next-month nav button. Default 'Next month'. */
41
+ nextMonthLabel?: string;
42
+ /** aria-label for the clear button (when `clearable`). Default 'Clear date'. */
43
+ clearLabel?: string;
36
44
  }
37
45
 
38
46
  function pad(n: number): string {
@@ -109,7 +117,11 @@ export function FdyDatepicker(props: FdyDatepickerProps): JSX.Element {
109
117
  const maxDate: Date | null = useMemo((): Date | null => parseISO(props.max), [props.max]);
110
118
  const isDisabled: boolean = props.disabled === true;
111
119
  const isInvalid: boolean = props.invalid === true;
112
- const displayPlaceholder: string = props.placeholder ?? 'Pilih tanggal';
120
+ const displayPlaceholder: string = props.placeholder ?? 'Select date';
121
+ const showClear: boolean = props.clearable === true && selectedDate !== null && !isDisabled;
122
+ const prevMonthLabelText: string = props.prevMonthLabel ?? 'Previous month';
123
+ const nextMonthLabelText: string = props.nextMonthLabel ?? 'Next month';
124
+ const clearLabelText: string = props.clearLabel ?? 'Clear date';
113
125
 
114
126
  const [viewMonth, setViewMonth] = useState<Date>((): Date => startOfMonth(parseISO(props.value) ?? new Date()));
115
127
  const [focusDate, setFocusDate] = useState<Date>((): Date => parseISO(props.value) ?? new Date());
@@ -203,6 +215,13 @@ export function FdyDatepicker(props: FdyDatepickerProps): JSX.Element {
203
215
  closePanel(true);
204
216
  }
205
217
 
218
+ // Clear (reset to empty). Calls onChange('') — parseISO('') is null, so the placeholder shows again.
219
+ function clearValue(): void {
220
+ props.onChange('');
221
+ setOpen(false);
222
+ triggerRef.current?.focus();
223
+ }
224
+
206
225
  function openPanel(): void {
207
226
  if (isDisabled || open) return;
208
227
  const next: Date = selectedDate ?? focusDate ?? new Date();
@@ -307,21 +326,34 @@ export function FdyDatepicker(props: FdyDatepickerProps): JSX.Element {
307
326
  <span className={selectedDate === null ? 'fdy-datepicker__value fdy-datepicker__value--placeholder' : 'fdy-datepicker__value'}>
308
327
  {displayValue}
309
328
  </span>
310
- <span className="fdy-datepicker__icon" aria-hidden="true">
329
+ <span className={showClear ? 'fdy-datepicker__icon is-hidden' : 'fdy-datepicker__icon'} aria-hidden="true">
311
330
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
312
331
  <rect x="3" y="4" width="18" height="18" rx="2"></rect>
313
332
  <path d="M16 2v4M8 2v4M3 10h18"></path>
314
333
  </svg>
315
334
  </span>
316
335
  </button>
336
+ {showClear ? (
337
+ <button
338
+ type="button"
339
+ className="fdy-datepicker__clear"
340
+ aria-label={clearLabelText}
341
+ onMouseDown={(e: React.MouseEvent): void => e.preventDefault()}
342
+ onClick={clearValue}
343
+ >
344
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
345
+ <path d="M18 6 6 18M6 6l12 12"></path>
346
+ </svg>
347
+ </button>
348
+ ) : null}
317
349
 
318
350
  <div ref={panelRef} className="fdy-datepicker__panel" role="dialog" aria-modal="false" aria-labelledby={titleId} hidden={!open}>
319
351
  <div className="fdy-cal__head">
320
- <button type="button" className="fdy-cal__nav" aria-label="Bulan sebelumnya" onClick={(): void => setViewMonth(addMonths(viewMonth, -1))}>
352
+ <button type="button" className="fdy-cal__nav" aria-label={prevMonthLabelText} onClick={(): void => setViewMonth(addMonths(viewMonth, -1))}>
321
353
  &lsaquo;
322
354
  </button>
323
355
  <div id={titleId} className="fdy-cal__title">{monthFmt.format(viewMonth)}</div>
324
- <button type="button" className="fdy-cal__nav" aria-label="Bulan berikutnya" onClick={(): void => setViewMonth(addMonths(viewMonth, 1))}>
356
+ <button type="button" className="fdy-cal__nav" aria-label={nextMonthLabelText} onClick={(): void => setViewMonth(addMonths(viewMonth, 1))}>
325
357
  &rsaquo;
326
358
  </button>
327
359
  </div>
@@ -26,6 +26,11 @@ const props = defineProps<{
26
26
  format?: 'number' | 'percent' | 'currency';
27
27
  stacked?: boolean;
28
28
  legend?: 'auto' | 'always' | 'none';
29
+ /**
30
+ * Per-series colour override. Each entry is a semantic token name (primary/accent/success/
31
+ * warning/danger/info) OR a categorical slot 'chart-1'..'chart-8' to pin a series to the
32
+ * validated palette. Omit to use the default fixed-order chart palette.
33
+ */
29
34
  colors?: ReadonlyArray<string>;
30
35
  color?: string;
31
36
  center?: string | number;
@@ -35,6 +35,14 @@ const props = defineProps<{
35
35
  describedby?: string;
36
36
  id?: string;
37
37
  ariaLabelledby?: string;
38
+ /** Show a clear (×) button in the trigger when a date is set, so an optional date can be unset. Emits `''` via update:modelValue + change. Off by default. */
39
+ clearable?: boolean;
40
+ /** aria-label for the previous-month nav button. Default 'Previous month' — override for non-English UIs (month/weekday names already follow `locale`). */
41
+ prevMonthLabel?: string;
42
+ /** aria-label for the next-month nav button. Default 'Next month'. */
43
+ nextMonthLabel?: string;
44
+ /** aria-label for the clear button (when `clearable`). Default 'Clear date'. */
45
+ clearLabel?: string;
38
46
  }>();
39
47
 
40
48
  const emit = defineEmits<{
@@ -98,7 +106,13 @@ const minDate: ComputedRef<Date | null> = computed((): Date | null => parseISO(p
98
106
  const maxDate: ComputedRef<Date | null> = computed((): Date | null => parseISO(props.max));
99
107
  const isDisabled: ComputedRef<boolean> = computed((): boolean => props.disabled === true);
100
108
  const isInvalid: ComputedRef<boolean> = computed((): boolean => props.invalid === true);
101
- const displayPlaceholder: ComputedRef<string> = computed((): string => props.placeholder ?? 'Pilih tanggal');
109
+ const displayPlaceholder: ComputedRef<string> = computed((): string => props.placeholder ?? 'Select date');
110
+ const showClear: ComputedRef<boolean> = computed(
111
+ (): boolean => props.clearable === true && selectedDate.value !== null && isDisabled.value === false,
112
+ );
113
+ const prevMonthLabelText: ComputedRef<string> = computed((): string => props.prevMonthLabel ?? 'Previous month');
114
+ const nextMonthLabelText: ComputedRef<string> = computed((): string => props.nextMonthLabel ?? 'Next month');
115
+ const clearLabelText: ComputedRef<string> = computed((): string => props.clearLabel ?? 'Clear date');
102
116
 
103
117
  const view: Ref<Date> = ref(startOfMonth(selectedDate.value ?? new Date()));
104
118
  const focusDate: Ref<Date> = ref(selectedDate.value ?? new Date());
@@ -188,6 +202,14 @@ function pick(d: Date): void {
188
202
  closePanel(true);
189
203
  }
190
204
 
205
+ // Clear (reset to empty). Emits '' — parseISO('') is null, so the placeholder shows again.
206
+ function clearValue(): void {
207
+ emit('update:modelValue', '');
208
+ emit('change', '');
209
+ open.value = false;
210
+ triggerEl.value?.focus();
211
+ }
212
+
191
213
  function openPanel(): void {
192
214
  if (isDisabled.value || open.value) return;
193
215
  focusDate.value = selectedDate.value ?? focusDate.value ?? new Date();
@@ -297,19 +319,31 @@ onBeforeUnmount((): void => {
297
319
  @click="toggle"
298
320
  >
299
321
  <span class="fdy-datepicker__value" :class="{ 'fdy-datepicker__value--placeholder': selectedDate === null }">{{ displayValue }}</span>
300
- <span class="fdy-datepicker__icon" aria-hidden="true">
322
+ <span class="fdy-datepicker__icon" :class="{ 'is-hidden': showClear }" aria-hidden="true">
301
323
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
302
324
  <rect x="3" y="4" width="18" height="18" rx="2"></rect>
303
325
  <path d="M16 2v4M8 2v4M3 10h18"></path>
304
326
  </svg>
305
327
  </span>
306
328
  </button>
329
+ <button
330
+ v-if="showClear"
331
+ type="button"
332
+ class="fdy-datepicker__clear"
333
+ :aria-label="clearLabelText"
334
+ @mousedown.prevent
335
+ @click="clearValue"
336
+ >
337
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
338
+ <path d="M18 6 6 18M6 6l12 12"></path>
339
+ </svg>
340
+ </button>
307
341
 
308
342
  <div ref="panelEl" class="fdy-datepicker__panel" role="dialog" aria-modal="false" popover="manual" :aria-labelledby="titleId" :hidden="!open">
309
343
  <div class="fdy-cal__head">
310
- <button type="button" class="fdy-cal__nav" aria-label="Bulan sebelumnya" @click="view = addMonths(view, -1)">‹</button>
344
+ <button type="button" class="fdy-cal__nav" :aria-label="prevMonthLabelText" @click="view = addMonths(view, -1)">‹</button>
311
345
  <div :id="titleId" class="fdy-cal__title">{{ monthFmt.format(view) }}</div>
312
- <button type="button" class="fdy-cal__nav" aria-label="Bulan berikutnya" @click="view = addMonths(view, 1)">›</button>
346
+ <button type="button" class="fdy-cal__nav" :aria-label="nextMonthLabelText" @click="view = addMonths(view, 1)">›</button>
313
347
  </div>
314
348
  <div class="fdy-cal__grid" role="grid" :aria-labelledby="titleId" @keydown="onGridKeydown">
315
349
  <div v-for="w in weekdayHeaders" :key="w" class="fdy-cal__dow" role="columnheader" :aria-label="w">{{ w }}</div>
@@ -6,7 +6,9 @@
6
6
  * Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
7
7
  * semantic token by name (primary, accent, success, warning, danger, info). Multi-series
8
8
  * defaults draw from the validated categorical --chart-1..8 palette in fixed order (never
9
- * cycled, 8-series cap); pass data-fdy-colors to override with semantic names instead.
9
+ * cycled, 8-series cap); pass data-fdy-colors to override a name is a semantic token
10
+ * (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
11
+ * validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
10
12
  * Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
11
13
  * --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency).
12
14
  * Charts are an enhancement — put a table/text fallback inside the element and it will be
@@ -28,7 +30,14 @@
28
30
  // Categorical chart palette: 8 validated fixed-order slots (--chart-1..8). Series index i
29
31
  // (0-based) -> slot i+1; series beyond the 8-slot cap reuse --chart-8 (never cycled).
30
32
  function chartSlotVar(i) { return 'var(--chart-' + (i < 8 ? i + 1 : 8) + ')'; }
31
- function colorVar(name) { return 'var(--color-' + (name || 'primary') + ')'; }
33
+ // Resolve a colour name to a CSS var(). A semantic name (primary/accent/success/warning/
34
+ // danger/info) reaches --color-<name>; a slot name "chart-1".."chart-8" reaches the validated
35
+ // categorical token --chart-N instead, so a data-fdy-colors override can pin a series to the
36
+ // palette (stable per category) rather than being forced onto the nearest semantic hue.
37
+ function colorVar(name) {
38
+ name = name || 'primary';
39
+ return name.indexOf('chart-') === 0 ? 'var(--' + name + ')' : 'var(--color-' + name + ')';
40
+ }
32
41
 
33
42
  function nums(el, attr) {
34
43
  var v = el.getAttribute(attr);
@@ -848,6 +848,14 @@ a { color: var(--color-primary); }
848
848
  .fdy-datepicker__value--placeholder{color:var(--color-text-subtle);}
849
849
  .fdy-datepicker__icon{flex:none;display:inline-flex;color:var(--color-text-subtle);}
850
850
  .fdy-datepicker__icon svg{display:block;width:1.05rem;height:1.05rem;}
851
+ /* Clearable: a reset button overlaid in the icon slot (the trigger is itself a <button>, so the
852
+ clear control is a sibling, not a child). Shown only when clearable + a date is set; the calendar
853
+ icon is hidden (kept in flow so the value's ellipsis boundary doesn't shift). */
854
+ .fdy-datepicker__icon.is-hidden{visibility:hidden;}
855
+ .fdy-datepicker__clear{position:absolute;top:50%;right:var(--space-3);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;transition:color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard);}
856
+ .fdy-datepicker__clear:hover{color:var(--color-text);background:var(--color-surface-2);}
857
+ .fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
858
+ .fdy-datepicker__clear svg{display:block;width:.9rem;height:.9rem;}
851
859
 
852
860
  .fdy-datepicker__panel{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;padding:var(--space-3);background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
853
861
  .fdy-datepicker__panel[hidden]{display:none;}
package/dist/freeday.css CHANGED
@@ -503,6 +503,14 @@ a { color: var(--color-primary); }
503
503
  .fdy-datepicker__value--placeholder{color:var(--color-text-subtle);}
504
504
  .fdy-datepicker__icon{flex:none;display:inline-flex;color:var(--color-text-subtle);}
505
505
  .fdy-datepicker__icon svg{display:block;width:1.05rem;height:1.05rem;}
506
+ /* Clearable: a reset button overlaid in the icon slot (the trigger is itself a <button>, so the
507
+ clear control is a sibling, not a child). Shown only when clearable + a date is set; the calendar
508
+ icon is hidden (kept in flow so the value's ellipsis boundary doesn't shift). */
509
+ .fdy-datepicker__icon.is-hidden{visibility:hidden;}
510
+ .fdy-datepicker__clear{position:absolute;top:50%;right:var(--space-3);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;transition:color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard);}
511
+ .fdy-datepicker__clear:hover{color:var(--color-text);background:var(--color-surface-2);}
512
+ .fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
513
+ .fdy-datepicker__clear svg{display:block;width:.9rem;height:.9rem;}
506
514
 
507
515
  .fdy-datepicker__panel{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;padding:var(--space-3);background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
508
516
  .fdy-datepicker__panel[hidden]{display:none;}
package/dist/freeday.js CHANGED
@@ -779,7 +779,9 @@
779
779
  * Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
780
780
  * semantic token by name (primary, accent, success, warning, danger, info). Multi-series
781
781
  * defaults draw from the validated categorical --chart-1..8 palette in fixed order (never
782
- * cycled, 8-series cap); pass data-fdy-colors to override with semantic names instead.
782
+ * cycled, 8-series cap); pass data-fdy-colors to override a name is a semantic token
783
+ * (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
784
+ * validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
783
785
  * Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
784
786
  * --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency).
785
787
  * Charts are an enhancement — put a table/text fallback inside the element and it will be
@@ -801,7 +803,14 @@
801
803
  // Categorical chart palette: 8 validated fixed-order slots (--chart-1..8). Series index i
802
804
  // (0-based) -> slot i+1; series beyond the 8-slot cap reuse --chart-8 (never cycled).
803
805
  function chartSlotVar(i) { return 'var(--chart-' + (i < 8 ? i + 1 : 8) + ')'; }
804
- function colorVar(name) { return 'var(--color-' + (name || 'primary') + ')'; }
806
+ // Resolve a colour name to a CSS var(). A semantic name (primary/accent/success/warning/
807
+ // danger/info) reaches --color-<name>; a slot name "chart-1".."chart-8" reaches the validated
808
+ // categorical token --chart-N instead, so a data-fdy-colors override can pin a series to the
809
+ // palette (stable per category) rather than being forced onto the nearest semantic hue.
810
+ function colorVar(name) {
811
+ name = name || 'primary';
812
+ return name.indexOf('chart-') === 0 ? 'var(--' + name + ')' : 'var(--color-' + name + ')';
813
+ }
805
814
 
806
815
  function nums(el, attr) {
807
816
  var v = el.getAttribute(attr);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cahyo-dimas/freeday",
3
- "version": "1.11.2",
3
+ "version": "1.13.0",
4
4
  "description": "Freeday — token-driven, framework-agnostic UI KIT (design source-of-truth).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -11,6 +11,14 @@
11
11
  .fdy-datepicker__value--placeholder{color:var(--color-text-subtle);}
12
12
  .fdy-datepicker__icon{flex:none;display:inline-flex;color:var(--color-text-subtle);}
13
13
  .fdy-datepicker__icon svg{display:block;width:1.05rem;height:1.05rem;}
14
+ /* Clearable: a reset button overlaid in the icon slot (the trigger is itself a <button>, so the
15
+ clear control is a sibling, not a child). Shown only when clearable + a date is set; the calendar
16
+ icon is hidden (kept in flow so the value's ellipsis boundary doesn't shift). */
17
+ .fdy-datepicker__icon.is-hidden{visibility:hidden;}
18
+ .fdy-datepicker__clear{position:absolute;top:50%;right:var(--space-3);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;transition:color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard);}
19
+ .fdy-datepicker__clear:hover{color:var(--color-text);background:var(--color-surface-2);}
20
+ .fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
21
+ .fdy-datepicker__clear svg{display:block;width:.9rem;height:.9rem;}
14
22
 
15
23
  .fdy-datepicker__panel{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;padding:var(--space-3);background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
16
24
  .fdy-datepicker__panel[hidden]{display:none;}
@@ -6,7 +6,9 @@
6
6
  * Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
7
7
  * semantic token by name (primary, accent, success, warning, danger, info). Multi-series
8
8
  * defaults draw from the validated categorical --chart-1..8 palette in fixed order (never
9
- * cycled, 8-series cap); pass data-fdy-colors to override with semantic names instead.
9
+ * cycled, 8-series cap); pass data-fdy-colors to override a name is a semantic token
10
+ * (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
11
+ * validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
10
12
  * Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
11
13
  * --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency).
12
14
  * Charts are an enhancement — put a table/text fallback inside the element and it will be
@@ -28,7 +30,14 @@
28
30
  // Categorical chart palette: 8 validated fixed-order slots (--chart-1..8). Series index i
29
31
  // (0-based) -> slot i+1; series beyond the 8-slot cap reuse --chart-8 (never cycled).
30
32
  function chartSlotVar(i) { return 'var(--chart-' + (i < 8 ? i + 1 : 8) + ')'; }
31
- function colorVar(name) { return 'var(--color-' + (name || 'primary') + ')'; }
33
+ // Resolve a colour name to a CSS var(). A semantic name (primary/accent/success/warning/
34
+ // danger/info) reaches --color-<name>; a slot name "chart-1".."chart-8" reaches the validated
35
+ // categorical token --chart-N instead, so a data-fdy-colors override can pin a series to the
36
+ // palette (stable per category) rather than being forced onto the nearest semantic hue.
37
+ function colorVar(name) {
38
+ name = name || 'primary';
39
+ return name.indexOf('chart-') === 0 ? 'var(--' + name + ')' : 'var(--color-' + name + ')';
40
+ }
32
41
 
33
42
  function nums(el, attr) {
34
43
  var v = el.getAttribute(attr);