@cahyo-dimas/freeday 3.14.1 → 3.14.2

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
@@ -9,6 +9,31 @@ benar. Perubahan seperti itu ditulis di bawah `### Changed: BREAKING (types)`
9
9
  lama → tipe baru, dan cara menyempitkannya — bukan di bawah `### Added`, betapapun aditifnya dari
10
10
  sisi kit.
11
11
 
12
+ ## [3.14.2] - 2026-10-30
13
+
14
+ **`useFreeday(ref)` dengan ref yang masih kosong kini bersuara.** Ref yang lupa dipasang di template
15
+ membuat adapter memindai seluruh dokumen tanpa tanda apa pun. Hasilnya bekerja, hanya lebih luas
16
+ dari yang diminta, dan markup lain di halaman diam-diam ikut bergantung padanya sampai hari ref itu
17
+ dipasang (`CATATAN-FREEDAY.md` §17). Kebalikan dari cakupan yang terlalu sempit di 3.13.0 `#099`.
18
+
19
+ ### Added
20
+ - **Vue dan React: satu `console.warn` per komponen ketika ref yang diberikan ke `useFreeday` masih
21
+ `null` saat mount** (`#105`). Di Vue diperiksa di `onMounted`, di React sesudah commit pertama;
22
+ pembaruan sesudahnya tidak mengulangnya. Pesannya menyebut dua penyebab (ref tak terpasang, atau
23
+ terpasang di elemen di balik `v-if`/kondisi) dan dua jalan keluarnya (pasang di elemen yang selalu
24
+ dirender, atau `useFreeday()` tanpa argumen bila seluruh dokumen memang yang dimaksud).
25
+
26
+ ### Catatan
27
+ - **Yang dipindai tidak berubah.** Ref kosong tetap jatuh ke seluruh dokumen. Adapter tidak dapat
28
+ membedakan ref yang lupa dari ref yang sekadar terlambat, dan mempersempitnya sekarang akan
29
+ mematikan dengan diam markup yang hari ini hanya hidup karena pemindaian itu, persis kelas cacat
30
+ `#099`. Peringatan ini memberi aplikasi waktu untuk membereskannya lebih dulu.
31
+ - Peringatan dinyalakan di setiap build, bukan hanya mode dev, sama seperti
32
+ `freeday-unhydrated.js`: kit tidak punya bendera build, dan peringatan ini hanya muncul pada kode
33
+ yang memang keliru.
34
+ - Blazor tidak dicakup: `ElementReference` adalah struct, bukan ref yang dapat `null`, dan apa yang
35
+ sampai ke `FreedayBlazor.initAll` dari `@ref` yang lupa ditangkap belum diukur.
36
+
12
37
  ## [3.14.1] - 2026-10-30
13
38
 
14
39
  **Badan `FdyModal` kembali setinggi isinya di iPhone.** Di iPhone 7 sungguhan (iOS 15), setiap modal
package/COMPONENTS.md CHANGED
@@ -52,6 +52,11 @@ live docs.
52
52
  never enhanced logs one `console.warn` naming it, with the element attached. The usual cause is
53
53
  a `useFreeday()` ref on a wrapper that does not contain it. `freeday-unhydrated.js` does this,
54
54
  and it ships in the root import.
55
+ **A `useFreeday(ref)` whose ref is still null at mount says so too.** That is a ref left off the
56
+ template, or one on an element behind `v-if` / a condition, and the call falls back to the whole
57
+ document: everything works, wider than asked, and other markup on the page can come to rely on
58
+ that scan unnoticed. It logs one `console.warn` per component. Put the ref on an element that is
59
+ always rendered, or write `useFreeday()` with no argument when the document is what you mean.
55
60
  **`data-fdy-ignore` says "not yours".** On an element or any ancestor, every enhancer skips that
56
61
  subtree, even under an explicit `initAll(document)`, and the warning above stays quiet. Use it
57
62
  where the app drives kit-styled markup itself, such as a chip matrix with its own removal flow.
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-v3.14.1-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.14.1)
8
+ [![Release](https://img.shields.io/badge/release-v3.14.2-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.14.2)
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-v3.14.1-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.14.1)
8
+ [![Release](https://img.shields.io/badge/release-v3.14.2-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.14.2)
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`.
@@ -8,7 +8,8 @@ export interface UseFreedayReturn {
8
8
  /**
9
9
  * Auto-initialise Freeday enhancers over a component's subtree on mount and
10
10
  * after every commit. Pass a ref to scope the work; omit it to scan the whole
11
- * document.
11
+ * document. A ref that is still null after the first commit also scans the
12
+ * whole document, and warns once: bind it to an always-rendered element, or omit it.
12
13
  */
13
14
  export declare function useFreeday(rootRef?: RefObject<HTMLElement | null>): UseFreedayReturn;
14
15
 
@@ -14,10 +14,16 @@
14
14
  * well as descendants, so both shapes work. (Before 1.23.0 only the wrapping shape did, and
15
15
  * the other failed silently: querySelectorAll never matches its root.)
16
16
  *
17
+ * A ref that is still null after the first commit is a mistake, and an unusually quiet one: the
18
+ * call falls back to the whole document, so everything works, only wider than asked, and other code
19
+ * on the page can come to depend on that scan without knowing it. It warns once, after the first
20
+ * commit. Pass no argument, useFreeday(), when the whole document is what you mean; and put the ref
21
+ * on an element that is always rendered, not one behind a condition.
22
+ *
17
23
  * Events are native bubbling CustomEvents, add listeners on the root (they
18
24
  * bubble) and read event.detail. See index.d.ts for the detail types.
19
25
  */
20
- import { useEffect, useCallback } from 'react';
26
+ import { useEffect, useCallback, useRef } from 'react';
21
27
 
22
28
  // Discover Freeday enhancer namespaces once (all registered by the time an app
23
29
  // mounts, since `import 'freeday'` runs first). Each exposes initAll(el?).
@@ -36,7 +42,13 @@ function freedayApis() {
36
42
  return _apis;
37
43
  }
38
44
 
45
+ var EMPTY_REF = 'Freeday: useFreeday(ref) got a ref that is still null after the first commit, so it hydrated '
46
+ + 'the whole document instead of one subtree. Either ref={...} is missing from the JSX, or it sits on an '
47
+ + 'element that is not rendered yet (a condition). Put the ref on an element that is always rendered, or '
48
+ + 'call useFreeday() with no argument if the whole document is what you mean.';
49
+
39
50
  export function useFreeday(rootRef) {
51
+ var checked = useRef(false);
40
52
  var rehydrate = useCallback(function () {
41
53
  var el = rootRef && rootRef.current ? rootRef.current : undefined;
42
54
  var apis = freedayApis();
@@ -44,6 +56,12 @@ export function useFreeday(rootRef) {
44
56
  }, [rootRef]);
45
57
  // No dependency array: runs after every commit (mount + updates), mirroring
46
58
  // the Vue adapter's onMounted + onUpdated.
47
- useEffect(rehydrate);
59
+ useEffect(function () {
60
+ if (!checked.current) {
61
+ checked.current = true;
62
+ if (rootRef && !rootRef.current) console.warn(EMPTY_REF);
63
+ }
64
+ rehydrate();
65
+ });
48
66
  return { rehydrate: rehydrate };
49
67
  }
@@ -8,7 +8,8 @@ export interface UseFreedayReturn {
8
8
  /**
9
9
  * Auto-initialise Freeday enhancers over a component's subtree on mount and
10
10
  * after every update. Pass a template ref to scope the work; omit it to scan
11
- * the whole document.
11
+ * the whole document. A ref that is still null at mount also scans the whole
12
+ * document, and warns once: bind it to an always-rendered element, or omit it.
12
13
  */
13
14
  export declare function useFreeday(rootRef?: Ref<HTMLElement | null>): UseFreedayReturn;
14
15
 
@@ -15,6 +15,12 @@
15
15
  * well as descendants, so both shapes work. (Before 1.23.0 only the wrapping shape did, and
16
16
  * the other failed silently: querySelectorAll never matches its root.)
17
17
  *
18
+ * A ref that is still null once the component has mounted is a mistake, and an unusually quiet
19
+ * one: the call falls back to the whole document, so everything works, only wider than asked, and
20
+ * other code on the page can come to depend on that scan without knowing it. It warns once, at
21
+ * mount. Pass no argument, useFreeday(), when the whole document is what you mean; and put the ref
22
+ * on an element that is always rendered, not one behind v-if.
23
+ *
18
24
  * Events come straight through as native bubbling CustomEvents, bind them with
19
25
  * plain v-on (e.g. @fdy-cascade-change) and read event.detail. See index.d.ts
20
26
  * for the detail types.
@@ -36,6 +42,11 @@ function freedayApis() {
36
42
  return apis;
37
43
  }
38
44
 
45
+ var EMPTY_REF = 'Freeday: useFreeday(ref) got a ref that is still null after mount, so it hydrated the whole '
46
+ + 'document instead of one subtree. Either ref="..." is missing from the template, or it sits on an element '
47
+ + 'that is not rendered yet (v-if). Put the ref on an element that is always rendered, or call useFreeday() '
48
+ + 'with no argument if the whole document is what you mean.';
49
+
39
50
  export function useFreeday(rootRef) {
40
51
  var apis = null;
41
52
  function rehydrate() {
@@ -43,7 +54,10 @@ export function useFreeday(rootRef) {
43
54
  var el = rootRef && 'value' in rootRef ? (rootRef.value || undefined) : undefined;
44
55
  for (var i = 0; i < apis.length; i++) apis[i].initAll(el);
45
56
  }
46
- onMounted(rehydrate);
57
+ onMounted(function () {
58
+ if (rootRef && 'value' in rootRef && !rootRef.value) console.warn(EMPTY_REF);
59
+ rehydrate();
60
+ });
47
61
  onUpdated(function () { nextTick(rehydrate); });
48
62
  return { rehydrate: rehydrate };
49
63
  }
@@ -198,7 +198,7 @@ live docs also have a copy button per component.
198
198
  ```bash
199
199
  npm i @cahyo-dimas/freeday
200
200
  ```
201
- Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.14.1"` (public npm package). `dist/` is
201
+ Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.14.2"` (public npm package). `dist/` is
202
202
  committed and published → no build step; `npm ci` runs without auth.
203
203
 
204
204
  ### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cahyo-dimas/freeday",
3
- "version": "3.14.1",
3
+ "version": "3.14.2",
4
4
  "description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -96,7 +96,7 @@
96
96
  "scripts": {
97
97
  "build": "node tokens/build.mjs",
98
98
  "test": "node --test",
99
- "test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/cfl-dialog-only.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/tabs-vertical.mjs browser/menu-align.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/table-footer-text.mjs browser/month-mode.mjs browser/monthpicker.mjs browser/page-columns.mjs browser/table-hover.mjs browser/typed-popover.mjs browser/filter-parity.mjs browser/filter-wrap.mjs browser/filter-stack.mjs browser/timepicker.mjs browser/form-kit-required.mjs browser/picker-in-modal.mjs browser/harness-contract.mjs browser/app-owned.mjs browser/sortable.mjs browser/modal-phone.mjs",
99
+ "test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/cfl-dialog-only.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/tabs-vertical.mjs browser/menu-align.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/table-footer-text.mjs browser/month-mode.mjs browser/monthpicker.mjs browser/page-columns.mjs browser/table-hover.mjs browser/typed-popover.mjs browser/filter-parity.mjs browser/filter-wrap.mjs browser/filter-stack.mjs browser/timepicker.mjs browser/form-kit-required.mjs browser/picker-in-modal.mjs browser/harness-contract.mjs browser/app-owned.mjs browser/sortable.mjs browser/modal-phone.mjs browser/empty-ref.mjs",
100
100
  "prepack": "node tokens/build.mjs",
101
101
  "version": "node tokens/build.mjs && git add dist",
102
102
  "typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",