tsgrid-ui 2.7.0 → 2.9.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/dist/base.d.ts +148 -0
  3. package/dist/base.es6.js +11 -0
  4. package/dist/base.es6.js.map +1 -0
  5. package/dist/chunks/chunk-26XP2XU3.js +1795 -0
  6. package/dist/chunks/chunk-26XP2XU3.js.map +1 -0
  7. package/dist/chunks/chunk-3NYH6545.js +2423 -0
  8. package/dist/chunks/chunk-3NYH6545.js.map +1 -0
  9. package/dist/chunks/chunk-BIB3X2TW.js +1638 -0
  10. package/dist/chunks/chunk-BIB3X2TW.js.map +1 -0
  11. package/dist/chunks/chunk-DXZJHS4M.js +1283 -0
  12. package/dist/chunks/chunk-DXZJHS4M.js.map +1 -0
  13. package/dist/chunks/chunk-EVZMMVXO.js +1212 -0
  14. package/dist/chunks/chunk-EVZMMVXO.js.map +1 -0
  15. package/dist/chunks/chunk-GJD5NFWQ.js +2305 -0
  16. package/dist/chunks/chunk-GJD5NFWQ.js.map +1 -0
  17. package/dist/chunks/chunk-IYF3Q7GX.js +127 -0
  18. package/dist/chunks/chunk-IYF3Q7GX.js.map +1 -0
  19. package/dist/chunks/chunk-OFASTA2A.js +2980 -0
  20. package/dist/chunks/chunk-OFASTA2A.js.map +1 -0
  21. package/dist/chunks/chunk-OMLGN735.js +677 -0
  22. package/dist/chunks/chunk-OMLGN735.js.map +1 -0
  23. package/dist/chunks/chunk-WKSLGUB3.js +1127 -0
  24. package/dist/chunks/chunk-WKSLGUB3.js.map +1 -0
  25. package/dist/chunks/chunk-YBY52G2U.js +849 -0
  26. package/dist/chunks/chunk-YBY52G2U.js.map +1 -0
  27. package/dist/field.d.ts +329 -0
  28. package/dist/field.es6.js +11 -0
  29. package/dist/field.es6.js.map +1 -0
  30. package/dist/form.d.ts +162 -0
  31. package/dist/form.es6.js +14 -0
  32. package/dist/form.es6.js.map +1 -0
  33. package/dist/layout.d.ts +108 -0
  34. package/dist/layout.es6.js +13 -0
  35. package/dist/layout.es6.js.map +1 -0
  36. package/dist/locale.d.ts +30 -0
  37. package/dist/locale.es6.js +7 -0
  38. package/dist/locale.es6.js.map +1 -0
  39. package/dist/metafile-esm.json +1 -0
  40. package/dist/popup.d.ts +92 -0
  41. package/dist/popup.es6.js +18 -0
  42. package/dist/popup.es6.js.map +1 -0
  43. package/dist/query-CKGg5Ugv.d.ts +81 -0
  44. package/dist/sidebar.d.ts +138 -0
  45. package/dist/sidebar.es6.js +11 -0
  46. package/dist/sidebar.es6.js.map +1 -0
  47. package/dist/tabs.d.ts +63 -0
  48. package/dist/tabs.es6.js +11 -0
  49. package/dist/tabs.es6.js.map +1 -0
  50. package/dist/toolbar.d.ts +97 -0
  51. package/dist/toolbar.es6.js +11 -0
  52. package/dist/toolbar.es6.js.map +1 -0
  53. package/dist/tooltip.d.ts +322 -0
  54. package/dist/tooltip.es6.js +18 -0
  55. package/dist/tooltip.es6.js.map +1 -0
  56. package/dist/tsgrid-ui.css +2 -2
  57. package/dist/tsgrid-ui.d.ts +16 -2004
  58. package/dist/tsgrid-ui.es6.js +7750 -23831
  59. package/dist/tsgrid-ui.es6.js.map +1 -1
  60. package/dist/tsgrid-ui.es6.min.js +28 -28
  61. package/dist/tsgrid-ui.js +103 -25
  62. package/dist/tsgrid-ui.min.css +2 -2
  63. package/dist/tsgrid-ui.min.js +24 -24
  64. package/dist/tsutils-message-CogFtVtO.d.ts +82 -0
  65. package/dist/utils.d.ts +418 -0
  66. package/dist/utils.es6.js +14 -0
  67. package/dist/utils.es6.js.map +1 -0
  68. package/package.json +26 -5
package/CHANGELOG.md CHANGED
@@ -2,6 +2,235 @@
2
2
 
3
3
  All notable changes to **TsGrid UI** will be documented in this file.
4
4
 
5
+ ## v2.8.0 — 2026-05-14
6
+
7
+ ### Added
8
+
9
+ - **Subpath exports** — 11 new `package.json` export entries enable tree-shakable imports
10
+ (amendment #983: `./grid` deferred to Phase 3 pending `splitting:true` code-split):
11
+ - `tsgrid-ui/locale` → `TsLocale` (~3,843 B, 99.6% smaller than barrel)
12
+ - `tsgrid-ui/base` → `TsBase`, `TsEvent`, `toSafeEvent` (~32,135 B, 96.6% smaller)
13
+ - `tsgrid-ui/utils` → `TsUi`, `TsUtils`, `query` (~127,812 B, 86.5% smaller)
14
+ - `tsgrid-ui/popup` → `TsPopup`, `TsAlert`, `TsConfirm`, `TsPrompt`, `TsDialog` (~159,775 B, 83.1% smaller)
15
+ - `tsgrid-ui/tooltip` → `TsTooltip`, `TsMenu`, `TsColor`, `TsDate`, `Tooltip` (~244,488 B, 74.1% smaller)
16
+ - `tsgrid-ui/tabs` → `TsTabs` (~268,343 B, 71.6% smaller)
17
+ - `tsgrid-ui/toolbar` → `TsToolbar` (~288,428 B, 69.5% smaller)
18
+ - `tsgrid-ui/sidebar` → `TsSidebar` (~306,229 B, 67.6% smaller)
19
+ - `tsgrid-ui/field` → `TsField` (~312,704 B, 66.9% smaller)
20
+ - `tsgrid-ui/layout` → `TsLayout` (~354,172 B, 62.5% smaller)
21
+ - `tsgrid-ui/form` → `TsForm` (~468,108 B, 50.5% smaller)
22
+ - **`./package.json` export** — enables tooling that reads package metadata at runtime.
23
+ - **`reports/bundle/v2.8.0-baseline.json`** — schemaVersion 2; per-subpath empirical byte
24
+ measurements. Phase 2 gate for the v3.0 tree-shakable roadmap.
25
+ - Grid users continue using the barrel import (`tsgrid-ui`); `./grid` subpath export deferred
26
+ to Phase 3 once `splitting:true` reduces transitive duplication.
27
+
28
+ ### Bundle
29
+
30
+ - Monolith `tsgrid-ui` entry unchanged at 945,470 bytes (byte-identical to v2.7.1).
31
+ - Subpath bundles emitted with `splitting: false`; shared modules re-emitted per subpath.
32
+ Most modern bundlers deduplicate transparently (esbuild, Rollup, Vite, webpack 5+, Parcel 2+).
33
+ - `splitting: true` shared-chunk optimization planned for v2.9 / Phase 3.
34
+ - Dist artifact count: 7 → 29 files (+11 ESM subpath bundles + 11 `.d.ts` files).
35
+
36
+ ### BC
37
+
38
+ - Public API surface: **purely additive**. The `"."` barrel entry is byte-identical to v2.7.1.
39
+ - `dist/tsgrid-ui.es6.js` SHA: byte-identical to v2.7.1 (INV-SX-6 PASS).
40
+ - `src/index.ts` barrel: byte-identical to v2.7.1 (INV-7 PASS).
41
+ - No `require:` condition on subpaths — CJS consumers continue using `'tsgrid-ui'` barrel.
42
+ CJS subpaths planned for Phase 4 (requires `wrap-legacy.mjs` per-symbol refactoring).
43
+ - No barrel deprecation: `'tsgrid-ui'` barrel is fully supported with zero deprecation markers.
44
+ - SEMVER MINOR per SemVer §7. No breaking changes.
45
+
46
+ ### Tests
47
+
48
+ - `test/unit/subpath-exports.test.ts` (NEW) — 3 groups: exports shape, dist existence,
49
+ bundle floor. All 3 groups GREEN from Phase 6 onward.
50
+ - `test/consumer-smoke.ts` — extended with 11 named-import probes + 11 type-only probes.
51
+ - Total Vitest tests: **352/352 GREEN** (v2.7.1 baseline: 301/301 — +13 new assertions
52
+ plus expanded parametric tests via `it.each`).
53
+ - `tsup.config.analyze.ts`: UNCHANGED (INV-ANALYZE-ISOLATION PASS).
54
+
55
+ ## v2.9.0 — 2026-05-15
56
+
57
+ ### Added
58
+
59
+ - **`TsToolbar.toggle(...args): any[]` — new public API** (`src/tstoolbar.ts:481`): flips the `checked` state of one or more items without firing events or opening overlays. Complements `check()` / `uncheck()` / `click()`:
60
+ - `button` / `check` / `html` / `spacer` / `break`: flip `it.checked`.
61
+ - `drop` / `menu` / `menu-radio` / `menu-check` / `color` / `text-color`: if currently checked, closes the toolbar's `-drop` overlay via `TsTooltip.hide` before flipping (mirrors `uncheck()` overlay-close path). Never opens overlays.
62
+ - `radio`: `console.warn` + skip. Use `check()` / `uncheck()` for radios.
63
+ - `group`: recurses into `it.items`; the container itself is never in the effected list.
64
+ - `sub-id` with `:` notation and missing ids: silently skipped.
65
+ - Returns the array of ids whose state actually flipped (never `undefined`).
66
+ - State-only — does NOT fire `onClick` or `onChange`.
67
+
68
+ ### Fixed (toolbar polish-lifecycle)
69
+
70
+ - `setCount` no longer recurses into non-count item types — guarded at function entry (`7d721185`).
71
+ - `destroy()` now closes pending overlays before unmount, preventing detached tooltip refs (`01752f36`).
72
+ - `menu-check` items now seed `selected` from `it.items` function-form returns at insert time (`712a1bac`).
73
+
74
+ ### Fixed (toolbar polish-api)
75
+
76
+ - `get(id, true)` and `remove()` now compute the correct sub-item index inside group containers (`8a522974`, Smell 6).
77
+
78
+ ### Refactor (toolbar polish-api)
79
+
80
+ - Removed unreachable `item.type==null` guard in toolbar render path (`50c8f2ea`, Smell 2).
81
+
82
+ ### Style (toolbar polish-api)
83
+
84
+ - Single display value emitted for hidden-group sub-items (`2418f929`, Smell 4).
85
+ - Intentional switch fall-through explicitly annotated (`577bd566`, Smell 3).
86
+
87
+ ### Build / Dist
88
+
89
+ - Removed orphan chunks from analyze build (`459b8202`, closes WARNING-V1).
90
+ - Rebuilt CJS dist with v2.8.1 banner (`48960492`, closes WARNING-V2).
91
+ - Rebuilt `dist/` after polish-api merge — restored coherence (`35fd76ba`, closes WARNING-V4).
92
+
93
+ ### BC
94
+
95
+ - Public API surface: **purely additive**. `TsToolbar.toggle()` is new; all existing methods unchanged.
96
+ - SEMVER MINOR (new public API on `TsToolbar`).
97
+
98
+ ### Internal
99
+
100
+ - Verify-pass for v2.8.1 (`2bcb55a3`) was completed on the `feature/v2.8.1-chunk-splitting-side-effects` branch before polish-lifecycle (3 fixes), polish-api Smells (2/3/4/6), and `toggle()` API landed on master. v2.9.0 packages those post-verify additions as a minor release.
101
+
102
+ ## v2.8.1 — 2026-05-15
103
+
104
+ ### Added
105
+
106
+ #### Bundle
107
+
108
+ - **`sideEffects` per-file map** — `package.json` now declares an explicit 9-entry
109
+ `sideEffects` array, enabling webpack and rollup to tree-shake unused subpaths:
110
+ - Side-effectful (must load): `tsgrid-ui.css`, `tsgrid-ui.min.css`,
111
+ `utils.es6.js`, `popup.es6.js`, `tooltip.es6.js`, `tsgrid-ui.es6.js`,
112
+ `tsgrid-ui.es6.min.js`, `tsgrid-ui.js`, `tsgrid-ui.min.js`.
113
+ - Implicitly pure (may tree-shake): `locale.es6.js`, `base.es6.js`,
114
+ `tabs.es6.js`, `toolbar.es6.js`, `sidebar.es6.js`, `field.es6.js`,
115
+ `layout.es6.js`, `form.es6.js`, and all `dist/chunks/*.js`.
116
+ - **IMPORTANT**: esbuild (and therefore vite, tsup) does **NOT** honor
117
+ `package.json sideEffects`. Only webpack and rollup benefit from this field.
118
+ esbuild's own tree-shaking is based solely on static analysis of ESM imports.
119
+
120
+ - **ESM shared chunks under `dist/chunks/`** — `splitting: true` is now active in the
121
+ ESM non-min block (`tsup.config.ts` block 1). esbuild extracts shared code into
122
+ `dist/chunks/chunk-<8CHAR>.js` files automatically. With 12 entry points, 10 chunks
123
+ are produced. Consumers importing multiple subpaths load shared code once.
124
+
125
+ - **Multi-subpath byte savings** (webpack/rollup consumers):
126
+ - SC-A (`./popup` + `./form`): **19.8% reduction** (627,883 B → 503,563 B effective)
127
+ - SC-B (`./popup` + `./tooltip` + `./tabs`): **54.9% reduction** (672,606 B → 303,119 B)
128
+ - SC-C (`./locale` only): +123 B overhead (+3.2%, well within ≤1 KB canary threshold)
129
+ - SC-D (monolith `.`): +2,348 B overhead (+0.25%, within ±2% AC10 allowance)
130
+ - Full scenario table: `reports/bundle/v2.8.1-splitting-savings.md`
131
+
132
+ - **Cycle 5 forward reference** — `dist/utils.es6.js`, `dist/popup.es6.js`, and
133
+ `dist/tooltip.es6.js` remain in the `sideEffects` array because their constructors
134
+ run module-level side effects at import time (global singleton registration).
135
+ Cycle 5 (singleton lazy-init refactor) will remove these from the array, unlocking
136
+ additional savings for esbuild/vite consumers that currently cannot benefit.
137
+
138
+ - **`dist/chunks/` must ship with the package** — consumers installing tsgrid-ui from
139
+ npm must ensure `dist/chunks/*.js` files are present. The `package.json files` array
140
+ already covers `dist/` recursively; no additional configuration needed.
141
+
142
+ ### BC
143
+
144
+ - Public API surface: **purely additive**. All existing `import` paths are unchanged.
145
+ `dist/chunks/` is a new subdirectory but is an implementation detail — not a public API.
146
+ - SEMVER PATCH. No breaking changes.
147
+
148
+ ---
149
+
150
+ ### Fixed (build)
151
+ - `scripts/wrap-legacy.mjs`: `buildHeader()` is now deterministic — per-build
152
+ `new Date().toLocaleString('en-us')` timestamp removed. Version string is now
153
+ read dynamically from `package.json` via `readFile + JSON.parse` (was stale
154
+ hardcoded `1.0.x (nightly)`). CJS dist artifacts (`dist/tsgrid-ui.js`,
155
+ `dist/tsgrid-ui.min.js`) are now bit-identical across consecutive rebuilds
156
+ on unchanged source.
157
+ - Anti-regression: new Vitest unit test at `test/unit/wrap-legacy.test.ts`
158
+ guards against reintroduction of `new Date(`, `Date.now(`, `Math.random(`
159
+ in the post-build script.
160
+
161
+ ### Notes (build)
162
+ - SEMVER: chore — no version bump.
163
+ - Supersedes the CJS-SHA Option-B caveat from the bundle-baseline-instrumentation
164
+ cycle (spec #960 INV-BBI-1). Future cycles MAY assume strict CJS-SHA equality
165
+ across all 5 dist artifacts.
166
+
167
+ ### Added (tooling)
168
+ - `pnpm bundle:analyze` — generate esbuild metafile + per-module advisory summary (`reports/bundle/latest.md`, gitignored).
169
+ - `pnpm bundle:snapshot -- --version=vX.Y.Z` — write committed baseline JSON to `reports/bundle/<version>-baseline.json`.
170
+ - `tsup.config.analyze.ts` — analyze-only tsup config (single ESM-non-min block + `metafile: true`); structurally isolated from `tsup.config.ts` (INV-ANALYZE-ISOLATION).
171
+ - `reports/bundle/v2.7.1-baseline.json` — v2.7.1 ESM-non-min per-module byte composition. This artifact unlocks Phase 2 (subpath exports) acceptance testing (INV-CYCLE-1-HARD-GATE).
172
+ - `reports/bundle/README.md` — schema + scope documentation.
173
+
174
+ ### Internal
175
+ - Zero new devDependencies. Zero changes to `src/`. Zero changes to `dist/*.js` on production builds (byte-identical, verified via `shasum` gate).
176
+ - SEMVER: chore / no version bump. Next version bump (v2.8.0) reserved for Phase 2 (subpath exports).
177
+
178
+ ## v2.7.1 — 2026-05-14
179
+
180
+ ### Fixed
181
+
182
+ - **R-LOC-V26-PRESERVED — `keepPhrases=false` now actually clears phrases** (`src/tsutils-locale.ts:133`, string branch): The v2.6.0 / v2.7.0 implementation `deps.extend({}, settings, TsLocale, { phrases: {} }, data)` was a deep-merge no-op on the phrases sub-tree — `extend` is deep and `extend(existingPhrases, {})` iterates `Object.keys({})` → zero keys, leaving pre-existing phrase keys intact. v2.7.1 replaces this with the spread-override-before-extend idiom: `const phrasesCleared = { ...settings, phrases: {} }; deps.extend({}, phrasesCleared, TsLocale, data)`. Pre-existing `settings.phrases` keys are NOW cleared before the `TsLocale` + `data` phrase-merge, aligning with the original code comment "clear phrases from language before merging" (in place since v2.6.0). Covered by `T-LOC-5` (flipped) + `INV-L7-PHRASES-CLEAR` (string clause). See engram #925 (discovery).
183
+ - **R-LOC-2 — `_locale()` no longer mutates the caller's input array** (`src/tsutils-locale.ts:80,86`, array branch): Calling `await Utils.locale(['en-us', 'ru-ru'])` previously rewrote the caller's array in-place to `['locale/en-us.json', 'locale/ru-ru.json']` (alias mutation via `const localeArr = locale as string[]` + `localeArr[ind] = file`). v2.7.1 uses `.map()` upfront for path expansion, producing a fresh internal array; the caller's original array reference and contents are byte-identical post-call. Covered by new `T-LOC-11` + `INV-L7-IMMUTABLE-INPUT`.
184
+ - **R-V271-4 — Array-branch `mergedSettings` init now actually pre-resets phrases** (`src/tsutils-locale.ts:79`, array branch): `let mergedSettings = deps.extend({}, settings, { phrases: {} })` had the same deep-merge no-op as R-LOC-V26-PRESERVED — pre-existing `settings.phrases` keys leaked into the initial fanout accumulator. v2.7.1 replaces it with `let mergedSettings = deps.extend({}, { ...settings, phrases: {} })`, aligning with the comment "Pre-reset phrases before fanout" (in place since v2.7.0). Covered by new `T-LOC-12` + `INV-L7-PHRASES-CLEAR` (array clause).
185
+
186
+ ### Tests
187
+
188
+ - `T-LOC-5` flipped + header rewritten: was `expect(phrases).toHaveProperty('stale', 'remove-me')`; now `expect(phrases).not.toHaveProperty('stale')`. Positive assertion `expect(phrases).toHaveProperty('Add new record')` retained (proves the `keepPhrases=false` branch still merges TsLocale defaults). Comment block removes the `R-LOC-V26-PRESERVED` preservation note and adds the `INV-L7-PHRASES-CLEAR` reference + "v2.7.1 PATCH: phrases-clear now works as documented" one-liner.
189
+ - `T-LOC-11` NEW: array-input immutability gate (`INV-L7-IMMUTABLE-INPUT`). Snapshots the caller's input array, awaits `_locale(input, ...)`, asserts `expect(input).toEqual(snapshot)` AND `expect(input).toBe(inputRef)`. Sub-assertion: `deps.fetch` was still called with expanded paths (`'locale/en-us.json'`, `'locale/ru-ru.json'`).
190
+ - `T-LOC-12` NEW: array-branch `mergedSettings` phrases-clear gate (`INV-L7-PHRASES-CLEAR`, array clause). Sets `TsUtils.settings.phrases = { stale: 'remove-me' }` pre-call; asserts post-call `result.settings.phrases` does NOT contain `stale`.
191
+ - Total Vitest tests: **299/299 GREEN** (v2.7.0 baseline: 297/297).
192
+
193
+ ### Bundle
194
+
195
+ Delta vs v2.7.0 baseline (`e8d9a74e`):
196
+
197
+ | Artifact | v2.7.0 | v2.7.1 | Δ bytes | Δ % |
198
+ |----------|--------|--------|---------|-----|
199
+ | `dist/tsgrid-ui.js` (CJS) | 947,274 B | 947,277 B | +3 B | +0.0003% |
200
+ | `dist/tsgrid-ui.es6.js` (ESM) | 945,466 B | 945,470 B | +4 B | +0.0004% |
201
+ | `dist/tsgrid-ui.min.js` (CJS min) | 509,260 B | 509,263 B | +3 B | +0.0003% |
202
+ | `dist/tsgrid-ui.es6.min.js` (ESM min) | 508,125 B | 508,129 B | +4 B | +0.0004% |
203
+ | `dist/tsgrid-ui.d.ts` | 94,446 B | 94,446 B | 0 B | 0.0000% |
204
+ | `dist/tsgrid-ui.css` | 246,980 B | 246,979 B | -1 B | -0.0004% |
205
+ | `dist/tsgrid-ui.min.css` | 229,707 B | 229,706 B | -1 B | -0.0004% |
206
+
207
+ All within ±2% gate. PASSED. JS bundles grew by +3–4 B (3 small fixes + new INV-L7-* inline comments — Terser strips comments in min bundles so min delta is tiny). The `d.ts` is **byte-identical** to v2.7.0 (INV-L7-API PASS — `locale(` signature unchanged). CSS delta is icon-noise (non-deterministic gulp regen, ±1 B).
208
+
209
+ ### BC
210
+
211
+ - `TsUtils.locale(locale, keepPhrases?, noMerge?): Promise<{ file: string; data: unknown } | void>` — signature **BYTE-IDENTICAL** to v2.7.0. Public API surface unchanged. `dist/tsgrid-ui.d.ts` `locale(` line unchanged (INV-L7-API PASS).
212
+ - **Behavior corrections** (PATCH-classifiable per SemVer §6 — implementation now matches documented intent in all three cases):
213
+ - `await Utils.locale(lang, false /* keepPhrases */)` no longer preserves pre-existing `settings.phrases` keys (now matches the in-code comment "clear phrases from language before merging").
214
+ - `await Utils.locale([...])` no longer mutates the input array (caller's array reference + contents are byte-identical post-call).
215
+ - The internal array-branch `mergedSettings` accumulator no longer leaks pre-existing phrase keys into the fanout.
216
+ - `src/index.ts` barrel — **byte-identical** to v2.7.0 (INV-7 PASS).
217
+ - SEMVER PATCH. BC verdict: NONE for consumers using the documented behavior; latent-bug-correction for consumers depending on the never-documented broken behavior (none identified in repo audit).
218
+
219
+ ### Known issues
220
+
221
+ - **R-LOC-3** (carry-forward from v2.6 / v2.7): Locale extraction does NOT directly unblock formatter extraction. The `format()` family still has its own deps-injection requirement; will be tackled separately in v2.8 via a `FormatterDeps` cluster.
222
+ - `R-LOC-V26-PRESERVED`, `R-LOC-2`, `R-V271-4` — **REMOVED** (now fixed; see §Fixed above).
223
+
224
+ ### Internal
225
+
226
+ - `INV-L7-PHRASES-CLEAR` (NEW invariant): When `keepPhrases=false` (string branch) OR array-branch init, the resulting `settings.phrases` MUST NOT contain keys from the pre-call `settings.phrases` that are absent from `TsLocale.phrases` (and `data.phrases` for the string branch). Gated by `T-LOC-5` (flipped) + `T-LOC-12` (new).
227
+ - `INV-L7-IMMUTABLE-INPUT` (NEW invariant): `_locale()` MUST NOT mutate the `locale` argument when it is an array. Caller's array reference + contents are byte-identical post-call. Gated by `T-LOC-11` (new).
228
+ - `INV-L7-LEAF` (carry, PASS): `tsutils-locale.ts` runtime imports limited to `./tslocale.js`; type-only from `./tsutils.js`. No tsbase/tsutils runtime imports.
229
+ - `INV-9` (carry, PASS): zero `this.X` references in `_locale()` body.
230
+ - `INV-LINT-INV8` canary (carry, PASS): `src/tsutils-*.ts` ESLint glob covers `tsutils-locale.ts`.
231
+
232
+ ---
233
+
5
234
  ## v2.7.0 — 2026-05-14
6
235
 
7
236
  ### Refactor
package/dist/base.d.ts ADDED
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Part of TsUi 2.0 library
3
+ * - Dependencies: TsUtils
4
+ * - on/off/trigger methods id not showing in help
5
+ * - refactored with event object
6
+ *
7
+ * Chanes in 2.0.+
8
+ * - added unmount that cleans up the box
9
+ *
10
+ */
11
+ /**
12
+ * Payload object passed to handlers registered via `.on(eventName, handler)`.
13
+ *
14
+ * IMPORTANT — circular references:
15
+ * `event.owner` points back to the widget that triggered the event, and that
16
+ * widget keeps `activeEvents: TsEvent[]` referencing this same payload.
17
+ * Calling `JSON.stringify(event)` will throw "Converting circular structure
18
+ * to JSON". Use `toSafeEvent(event)` from `tsgrid-ui` to extract a
19
+ * serializable subset before storing in reactive state (Angular signals,
20
+ * React state, Pinia/Redux stores, etc.).
21
+ *
22
+ * Note: the per-class declarations like `onSelect: (event: CustomEvent) => void`
23
+ * in TsGrid/TsForm/etc. are historical noise — the runtime always passes a
24
+ * `TsEventPayload`, never a DOM `CustomEvent`. This will be corrected in v2.0.
25
+ */
26
+ interface TsEventPayload<TDetail = unknown> {
27
+ type: string | null;
28
+ phase: 'before' | 'after' | string;
29
+ detail: TDetail & TsEventData;
30
+ target: unknown;
31
+ object: unknown;
32
+ isStopped: boolean;
33
+ isCancelled: boolean;
34
+ /** Reference to the widget that triggered this event. CIRCULAR — do not serialize. */
35
+ owner: unknown;
36
+ }
37
+ interface TsEventData {
38
+ type?: string | null;
39
+ target?: unknown;
40
+ phase?: string;
41
+ object?: unknown;
42
+ [key: string]: unknown;
43
+ }
44
+ interface TsEventListener {
45
+ name: string;
46
+ edata: {
47
+ type: string | null;
48
+ execute: string;
49
+ onComplete: null;
50
+ scope?: string;
51
+ handler?: unknown;
52
+ [key: string]: unknown;
53
+ };
54
+ handler: Function;
55
+ }
56
+ declare class TsEvent {
57
+ type: string | null;
58
+ detail: TsEventData;
59
+ owner: TsBase;
60
+ target: unknown;
61
+ phase: string;
62
+ object: unknown;
63
+ execute: null;
64
+ isStopped: boolean;
65
+ isCancelled: boolean;
66
+ onComplete: ((edata: TsEvent) => void) | null;
67
+ listeners: Array<(edata: TsEvent) => void>;
68
+ complete: Promise<TsEvent>;
69
+ _resolve: (value: TsEvent) => void;
70
+ _reject: (reason?: unknown) => void;
71
+ constructor(owner: TsBase, edata: TsEventData);
72
+ finish(detail?: Partial<TsEventData>): void;
73
+ done(func: (edata: TsEvent) => void): void;
74
+ preventDefault(): void;
75
+ stopPropagation(): void;
76
+ }
77
+ /**
78
+ * Extract a JSON-serializable subset of a TsEvent payload, dropping the
79
+ * circular `owner` and `complete` references. Use before storing in
80
+ * Angular signals, React state, Pinia/Redux stores, or any DevTools that
81
+ * snapshots state via JSON.
82
+ *
83
+ * @example
84
+ * grid.on('select', (event) => {
85
+ * this.lastSelection.set(toSafeEvent(event))
86
+ * })
87
+ */
88
+ declare function toSafeEvent<TDetail = unknown>(event: unknown): {
89
+ type: string | null;
90
+ phase: string;
91
+ detail: TDetail & TsEventData;
92
+ isStopped: boolean;
93
+ isCancelled: boolean;
94
+ };
95
+ declare class TsBase {
96
+ activeEvents: TsEvent[];
97
+ listeners: TsEventListener[];
98
+ debug: boolean;
99
+ name?: string;
100
+ box?: HTMLElement | null;
101
+ [key: string]: unknown;
102
+ /**
103
+ * Initializes base object for TsUi, registers it with TsUi object
104
+ *
105
+ * @param {string} name - name of the object
106
+ * @returns
107
+ */
108
+ constructor(name?: string);
109
+ /**
110
+ * Adds event listener, supports event phase and event scoping
111
+ *
112
+ * @param {*} edata - an object or string, if string "eventName:phase.scope"
113
+ * @param {*} handler
114
+ * @returns itself
115
+ */
116
+ on(events: string | TsEventData | Array<string | TsEventData>, handler: Function): this;
117
+ /**
118
+ * Removes event listener, supports event phase and event scoping
119
+ *
120
+ * @param {*} edata - an object or string, if string "eventName:phase.scope"
121
+ * @param {*} handler
122
+ * @returns itself
123
+ */
124
+ off(events: string | TsEventData | Array<string | TsEventData>, handler?: Function): this;
125
+ /**
126
+ * Triggers even listeners for a specific event, loops through this.listeners
127
+ *
128
+ * @param {Object} edata - Object
129
+ * @returns modified edata
130
+ *
131
+ * NOTE: `edata` is typed as `any` here intentionally. The method mutates the argument
132
+ * from TsEventData into a TsEvent mid-execution. Runtime type mutation is inherent
133
+ * to the event dispatch pattern. Phase 6 strict tighten will revisit this.
134
+ */
135
+ trigger(eventName: string | TsEventData | TsEvent, edataIn?: TsEventData): TsEvent;
136
+ /**
137
+ * This method renders component into the box. It is overwritten in descendents and in this base
138
+ * component it is empty.
139
+ */
140
+ render(_box?: HTMLElement | string | null): void;
141
+ /**
142
+ * Removes all classes that start with tsg-* and sets box to null. It is needed so that control will
143
+ * release the box to be used for other widgets
144
+ */
145
+ unmount(): void;
146
+ }
147
+
148
+ export { TsBase, TsEvent, type TsEventData, type TsEventListener, type TsEventPayload, toSafeEvent };
@@ -0,0 +1,11 @@
1
+ import {
2
+ TsBase,
3
+ TsEvent,
4
+ toSafeEvent
5
+ } from "./chunks/chunk-DXZJHS4M.js";
6
+ export {
7
+ TsBase,
8
+ TsEvent,
9
+ toSafeEvent
10
+ };
11
+ //# sourceMappingURL=base.es6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}