@popovandrii/ui-elements 0.1.3 → 0.2.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/README.md CHANGED
@@ -1,33 +1,144 @@
1
- # UI elements
2
- Version 0.0.31
1
+ # @popovandrii/ui-elements
3
2
 
4
- TypeScript-based NPM plugin.
5
- The plugin includes interface elements with specific properties.
6
- UI elements that contain a large share of JavaScript.
3
+ Lightweight TypeScript UI component library — **SpinBox**, **Select** (with search),
4
+ **Switch**, **ButtonGroup** (radio), **Button**.
7
5
 
8
- ## add JS
9
- ```js
10
- import { SpinBox, Switch, Select, ButtonGroup } from '@popovandrii/ui-elements';
6
+ - Zero runtime dependencies
7
+ - Vanilla TypeScript — works with any framework or none
8
+ - Accessible (ARIA / A11Y) with full keyboard support
9
+ - Themeable via CSS custom properties — 4 built-in themes
10
+ - SPA-friendly lifecycle: idempotent `scan()`, optional root scoping and `MutationObserver` auto re-scan, teardown-only `destroy()`
11
+ - Custom DOM events + programmatic `setValue`
12
+ - Ripple and flash animations (opt-out per element)
13
+ - Ships as ES, CJS and UMD bundles with type declarations
14
+
15
+ ## Components
16
+
17
+ | Component | Base class | Event | Docs |
18
+ |---|---|---|---|
19
+ | SpinBox | `UIsp` | `ui-spinbox-change` | [SpinBox](docs/spinBox/README.md) |
20
+ | Select | `UIselect` | `ui-select-change` | [Select](docs/select/README.md) |
21
+ | Switch | `UIsw` | `ui-switch-change` | [Switch](docs/switch/README.md) |
22
+ | ButtonGroup | `UIbg` | `ui-button-group-change` | [ButtonGroup (radio)](docs/button-group-radio/README.md) |
23
+ | Button | `UIb` | `ui-button-change` | [Button](docs/button/README.md) |
24
+
25
+ ## Installation
26
+
27
+ ```sh
28
+ npm install @popovandrii/ui-elements@latest
11
29
  ```
12
- Connection with `standard styles` from the package by element name.
30
+
31
+ ## Quick start
32
+
33
+ Import the components you need and the base stylesheet:
34
+
13
35
  ```js
14
- new SpinBox();
15
- new Switch();
36
+ import { SpinBox, Select, Switch, ButtonGroup, Button } from "@popovandrii/ui-elements";
37
+ import "@popovandrii/ui-elements/style.css";
38
+
39
+ // Each constructor scans the DOM for its base class and wires up matching elements.
40
+ new SpinBox();
16
41
  new Select();
42
+ new Switch();
43
+ new ButtonGroup();
44
+ new Button();
45
+ ```
46
+
47
+ Add the matching markup (see each component's docs for full examples):
48
+
49
+ ```html
50
+ <!-- data-decimals = number of decimal places to display (0 = integers) -->
51
+ <div class="UIsp" data-decimals="0" data-min="0" data-max="10" role="spinbutton" tabindex="0" aria-label="Numeric input">
52
+ <button class="UIsp__btn" type="button" aria-label="Decrease value">−</button>
53
+ <input class="UIsp__input" id="qty" type="text" value="0" inputmode="decimal">
54
+ <button class="UIsp__btn" type="button" aria-label="Increase value">+</button>
55
+ </div>
56
+ ```
57
+
58
+ ### Debug mode
59
+
60
+ Pass `true` as the second constructor argument to log selector/value warnings:
61
+
62
+ ```js
17
63
  new ButtonGroup({}, true); // debug mode
18
64
  ```
19
- Connection with `custom styles` from a package by element name.
65
+
66
+ ## Themes
67
+
68
+ Theming has two layers:
69
+
70
+ - **`style.css`** — required. Contains the component structure **and the default `light`
71
+ palette at `:root`**, so the components render correctly with just this file and no
72
+ `data-theme` set.
73
+ - **`theme-*.css`** — optional. Each provides one palette scoped under a higher-specificity
74
+ `html[data-theme="..."]` selector, so it overrides the `:root` default whenever the matching
75
+ `data-theme` is set — regardless of CSS import order.
76
+
77
+ Zero-config (light theme, no `data-theme` needed):
78
+
79
+ ```js
80
+ import "@popovandrii/ui-elements/style.css"; // that's it — light theme works
81
+ ```
82
+
83
+ Add theme files only when you need other palettes or runtime switching:
84
+
20
85
  ```js
21
- new SpinBox({
22
- main: "You-costom-class",
23
- btn: "You-costom-class__btn",
24
- input: "You-costom-class__input",
25
- disabledBtn: "You-costom-class__disabled",
26
- });
86
+ import "@popovandrii/ui-elements/style.css"; // required base (also = light default)
87
+
88
+ // import "@popovandrii/ui-elements/theme-dark.css";
89
+ // import "@popovandrii/ui-elements/theme-light-neon.css";
90
+ // import "@popovandrii/ui-elements/theme-dark-neon.css";
91
+ // import "@popovandrii/ui-elements/theme-light.css"; // only to switch *back* to light
92
+ ```
27
93
 
28
- new Switch({
29
- main: "You-costom-class",
30
- label: "You-costom-class-label"
94
+ ### Activate a theme with `data-theme`
95
+
96
+ `light` is the implicit default. To use another theme, load its file and set `data-theme` on a
97
+ parent — usually `<html>`:
98
+
99
+ ```html
100
+ <html data-theme="dark">
101
+ ```
102
+
103
+ Because themes are `html[data-theme="..."]` (higher specificity than `:root`), the explicit
104
+ theme always wins over the default; remove the attribute to fall back to `light`.
105
+
106
+ The `data-theme` values are: `light`, `dark`, `light-neon`, `dark-neon`.
107
+
108
+ ### Switching themes at runtime
109
+
110
+ Because each theme is scoped by its selector, you can import several and switch between them
111
+ by changing a single attribute:
112
+
113
+ ```js
114
+ document.documentElement.dataset.theme = "dark"; // light | dark | light-neon | dark-neon
115
+ ```
116
+
117
+ Themes are driven by CSS custom properties, so you can also override individual variables
118
+ in your own stylesheet instead of shipping a full theme.
119
+
120
+ ### Shared CSS modifier classes
121
+
122
+ Most components share a common set of modifier classes (see each component's docs for the
123
+ exact list it supports):
124
+
125
+ - **Color:** `primary`, `danger`, `info`, `success`, `warning`
126
+ - **Size:** `sm`, `lg` (Switch also has `xsm`)
127
+ - **Border radius:** `r-0`, `r-1` / `r-round`
128
+ - **Gap:** `g-0`, `g-1`
129
+ - **Layout:** `expand`, `between`, `around`, `vertical`, `border`
130
+
131
+ ## Custom class names
132
+
133
+ Every component accepts a partial selector map to match your own markup classes. Defaults
134
+ shown below:
135
+
136
+ ```js
137
+ new SpinBox({
138
+ main: "UIsp",
139
+ btn: "UIsp__btn",
140
+ input: "UIsp__input",
141
+ disabledBtn: "disabled",
31
142
  });
32
143
 
33
144
  new Select({
@@ -38,135 +149,247 @@ new Select({
38
149
  optionsList: "UIselect-options",
39
150
  search: "UIselect-options__search",
40
151
  items: "UIselect-options__items",
41
- excludedItems: ["divider", "test"], // class="costom"
152
+ flash: "UIselect--flash",
153
+ excludedItems: ["divider"], // <li> classes/ids excluded from selection
42
154
  });
43
155
 
44
- new ButtonGroup({
45
- main: "UIbg",
46
- btn: "UIbg-btn",
47
- input: "UIbg-input"
48
- });
156
+ new Switch({ main: "UIsw", label: "UIsw-label" });
157
+
158
+ new ButtonGroup({ main: "UIbg", btn: "UIbg-btn", input: "UIbg-input" });
159
+
160
+ new Button({ main: "UIb" });
49
161
  ```
50
162
 
51
- ## Example for NodeJS + Express (index.umd.js + style.css)
52
- ```sh
53
- npm install @popovandrii/ui-elements@latest
163
+ ## Events
164
+
165
+ Each component dispatches a bubbling `CustomEvent` whose `detail` is `{ id, value }`:
166
+
167
+ ```js
168
+ const el = document.getElementById("mySpinbox");
169
+ el.addEventListener("ui-spinbox-change", (e) => {
170
+ console.log(e.detail); // { id: "mySpinbox", value: "3.1415" }
171
+ });
54
172
  ```
173
+
174
+ | Component | Event name | `detail.value` |
175
+ |---|---|---|
176
+ | SpinBox | `ui-spinbox-change` | current numeric value as string |
177
+ | Select | `ui-select-change` | selected `data-value` |
178
+ | Switch | `ui-switch-change` | `"true"` / `"false"` |
179
+ | ButtonGroup | `ui-button-group-change` | selected `input[value]` |
180
+ | Button | `ui-button-change` | element's `data-value` |
181
+
182
+ ### TypeScript typing
183
+
184
+ ```ts
185
+ type UIChangeDetail = { id: string; value: string };
186
+
187
+ document.addEventListener("ui-select-change", (e) => {
188
+ const { id, value } = (e as CustomEvent<UIChangeDetail>).detail;
189
+ });
190
+ ```
191
+
192
+ ## Programmatic `setValue`
193
+
55
194
  ```js
56
- // app.js
57
- app.use(
58
- '/ui-elements/css',
59
- express.static(path.join(__dirname, 'node_modules/bootstrap/dist/css'))
60
- );
195
+ const sp = new SpinBox();
196
+ sp.setValue(document.getElementById("mySpinbox"), 3.1415);
197
+
198
+ const sl = new Select();
199
+ sl.setValue(document.getElementById("mySelect"), "name"); // matches data-value
200
+
201
+ const sw = new Switch();
202
+ sw.setValue("idSwitchElement", true); // (inputId, boolean)
203
+
204
+ const bg = new ButtonGroup();
205
+ bg.setValue(document.getElementById("myGroup"), "btncheck2"); // matches input[value]
206
+
207
+ const btn = new Button();
208
+ btn.setValue(document.getElementById("myButton"), "/api/users/42", "Edit profile"); // value + optional label
209
+ ```
210
+
211
+ ## Lifecycle (SPA-friendly)
212
+
213
+ Each instance attaches its listeners through a single `AbortController` created once in the
214
+ constructor. The three building blocks:
215
+
216
+ - **`scan()`** — binds every not-yet-bound element. It is **idempotent**: each control gets a
217
+ per-type marker (`data-uib-bound`, `data-uisel-bound`, `data-uisp-bound`, `data-uisw-bound`,
218
+ `data-uibg-bound`) and is skipped on later passes. Call it after rendering new markup — no
219
+ double-binding.
220
+ - **`destroy()`** — teardown only: aborts all listeners, disconnects the observer, clears the
221
+ markers and recreates the `AbortController` so the **same instance can `scan()` again**. It
222
+ does **not** disable elements (behavior change — use `setDisabled()` on Button for that).
223
+ - Markup-disabled elements (`data-disabled`, or `disabled` on the inner input) still render
224
+ disabled on every `scan()`.
225
+
226
+ ```js
227
+ const sl = new Select();
228
+ // ...after rendering new selects into the view:
229
+ sl.scan(); // additive — existing listeners untouched, new ones bound
230
+ // ...on final teardown:
231
+ sl.destroy(); // listeners removed; elements left as-is
232
+ ```
233
+
234
+ ### Scope to a root
235
+
236
+ Pass a third `options` argument to bind only inside a subtree. Useful when several managers of
237
+ the same type live on one page (e.g. a parent view plus a child component):
238
+
239
+ ```js
240
+ const widget = document.getElementById("cart-widget");
241
+ const buttons = new Button({}, false, { root: widget }); // ignores .UIb outside the widget
242
+ ```
243
+
244
+ ### Auto re-scan with `observe`
245
+
246
+ Add `observe: true` to attach a debounced `MutationObserver` on the root; new elements are
247
+ bound automatically, so you can drop manual `scan()` calls:
248
+
249
+ ```js
250
+ new Button({}, false, { root: appEl, observe: true });
251
+ ```
252
+
253
+ ### Singleton factory
254
+
255
+ If you'd rather not hold your own single instance per type, use the lazy factories:
256
+
257
+ ```js
258
+ import {
259
+ getButtonManager,
260
+ getSelectManager,
261
+ getSpinBoxManager,
262
+ getSwitchManager,
263
+ getButtonGroupManager,
264
+ resetManagers,
265
+ } from "@popovandrii/ui-elements";
266
+
267
+ getButtonManager({}, false, { observe: true }).scan(); // first call configures, then reused
268
+ resetManagers(); // drop all cached singletons
269
+ ```
270
+
271
+ The first call configures the manager (its arguments match the constructor); later calls
272
+ return the same instance.
273
+
274
+ ## Animations
275
+
276
+ - **Ripple** — radial wave on click
277
+ - **Flash** — `box-shadow` glow when `setValue` is called
278
+
279
+ Opt out per element or for any ancestor container:
280
+
281
+ ```html
282
+ <div class="UIsp ui-no-ripple ui-no-flash">...</div>
283
+
284
+ <!-- disable globally -->
285
+ <body class="ui-no-ripple ui-no-flash">
286
+ ```
287
+
288
+ ## Usage without a bundler (NodeJS + Express, UMD)
289
+
290
+ Serve the package's `dist` folder as static assets:
291
+
292
+ ```js
293
+ // app.js
61
294
  app.use(
62
- '/ui-elements/js',
63
- express.static(path.join(__dirname, 'node_modules/@popovandrii/ui-elements/dist'))
295
+ "/ui-elements/js",
296
+ express.static(path.join(__dirname, "node_modules/@popovandrii/ui-elements/dist"))
64
297
  );
65
298
  ```
299
+
66
300
  ```html
67
- <!-- you template ...ejs -->
68
- </head>
301
+ <head>
302
+ <link rel="stylesheet" href="/ui-elements/js/style.css" />
303
+ <link rel="stylesheet" href="/ui-elements/js/theme-dark.css" />
304
+ <!-- <link rel="stylesheet" href="/ui-elements/js/theme-light.css" /> -->
305
+ <!-- <link rel="stylesheet" href="/ui-elements/js/theme-light-neon.css" /> -->
306
+ <!-- <link rel="stylesheet" href="/ui-elements/js/theme-dark-neon.css" /> -->
69
307
  <script src="/ui-elements/js/index.umd.js"></script>
70
- <link rel='stylesheet' href="/ui-elements/js/style.css" />
71
- <link rel='stylesheet' href="/ui-elements/js/theme-dark.css" />
72
- <!-- <link rel='stylesheet' href="/ui-elements/js/theme-light.css" /> -->
73
- <!-- <link rel='stylesheet' href="/ui-elements/js/theme-neon-dark.css" /> -->
74
- <!-- <link rel='stylesheet' href="/ui-elements/js/theme-neon-light.css" /> -->
75
308
  </head>
76
309
 
77
- <!-- usually connected with a separate template -->
78
- <div class="UIsp" data-step="<%= step %>" data-min="<%= min %>" data-max="<%= max %>" role="spinbutton" tabindex="0"
79
- aria-label="Numeric input">
80
- <div class="UIsp-label"><%= label %></div>
81
- <button class="UIsp__btn" type="button" aria-label="Decrease value">svg icon</button>
82
- <input class="UIsp__input" id="<%= id %>" type="text" value="<%= min %>" aria-hidden="true" inputmode="decimal">
83
- <button class="UIsp__btn" type="button" aria-label="Increase value">svg icon</button>
84
- </div>
85
-
86
310
  <script>
87
- // the constructor can contain an object with your custom style
311
+ // UMD exposes a global `UiElements`
88
312
  new UiElements.SpinBox();
89
313
  new UiElements.Switch();
90
- new UiElements.Select();;
314
+ new UiElements.Select();
91
315
  </script>
92
316
  ```
93
317
 
94
- ## add Style
95
- ```js
96
- import '@popovandrii/ui-elements/style.css';
97
- // 4 design styles available.
98
- // theme-dark.css theme-neon-dark.css theme-neon-light.css
99
- import '@popovandrii/ui-elements/theme-light.css';
100
- ```
101
-
102
- Abbreviation of the base (main) class of each element:<br>
103
- `UIsp`, UIdd, UIsw ... (`UI + Spin-Box`, UI + Drop-Down, UI + Switch ...)<br>
104
- `<div class="UIsp">...</div>`
318
+ ## Documentation
105
319
 
106
- ### UI Elements
320
+ Each component has detailed docs covering markup, attributes, events, disabled states and
321
+ examples:
107
322
 
323
+ - [SpinBox](docs/spinBox/README.md) — numeric input with +/− buttons, min/max, decimal precision
324
+ - [Select](docs/select/README.md) — dropdown with optional search and keyboard navigation
325
+ - [Switch](docs/switch/README.md) — toggle based on `<input type="checkbox">`
326
+ - [ButtonGroup (radio)](docs/button-group-radio/README.md) — radio group styled as buttons
327
+ - [Button](docs/button/README.md) — styled button / link with event dispatch
108
328
 
109
- ### Button group UI
110
329
  <p align="center">
111
- <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/button-group-UI.png" alt="Switch Preview" width="700">
330
+ <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/button-group-UI.png" alt="ButtonGroup Preview" width="700">
112
331
  </p>
113
-
114
- #### [Button group README (radio)](/docs/button-group-radio/README.md)
115
-
116
- ### Button UI
117
332
  <p align="center">
118
- <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/button-UI.png" alt="Switch Preview" width="700">
333
+ <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/select-UI.png" alt="Select Preview" width="700">
119
334
  </p>
120
335
 
121
- #### [Button README (button)](/docs/button/README.md)
336
+ ## Development
122
337
 
123
- ### Switch UI
124
- <p align="center">
125
- <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/switch-UI.png" alt="Switch Preview" width="700">
126
- </p>
338
+ This project runs inside Docker (`node:24-alpine`); Node/npm are not required on the host.
127
339
 
128
- #### [Switch README (input)](/docs/switch/README.md)
340
+ ```sh
341
+ git clone git@gitlab.com:AndreyPopov/ui-elements.git && cd ui-elements
129
342
 
130
- ### SpinBox UI
131
- <p align="center">
132
- <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/spinBox-UI.png" alt="Switch Preview" width="700">
133
- </p>
343
+ docker compose up -d --build # start the dev container
344
+ docker compose exec vite-dev sh # shell into it
345
+ ```
134
346
 
135
- #### [SpinBox README (button -> input)](/docs/spinBox/README.md)
347
+ Inside the container:
136
348
 
137
- #### Select UI
349
+ ```sh
350
+ npm install
351
+ npm run dev # vite dev playground on port 5173
352
+ npm run build # build to ./dist (node build.js)
353
+ npm run test # vitest
354
+ npm run lint # eslint
355
+ npm run format # prettier
356
+ ```
138
357
 
139
- <p align="center">
140
- <img src="https://gitlab.com/AndreyPopov/ui-elements/-/raw/main/docs/images/select-UI.png" alt="Switch Preview" width="700">
141
- </p>
358
+ The `./playground` folder hosts the browser demo and unit tests.
142
359
 
143
- #### [Select README](/docs/select/README.md)
360
+ ### Publishing
144
361
 
145
- # Information for Developers
146
362
  ```sh
147
- git clone git@gitlab.com:AndreyPopov/ui-elements.git && cd ui-elements
363
+ npm install # refresh package-lock.json
364
+ npm login
365
+ npm publish --access public
366
+ npm info @popovandrii/ui-elements@latest
148
367
  ```
149
368
 
150
- ```sh
151
- docker compose up -d --build
152
- ```
369
+ ### Releasing
370
+
371
+ `dist/` is git-ignored, so always build before packing or publishing. Tags use the
372
+ `vX.Y.Z` scheme and point at the merge commit on `main`.
153
373
 
154
374
  ```sh
155
- docker compose exec vite-dev sh
375
+ # 1. Bump the version on dev (no auto-tag — we tag on main after the merge)
376
+ npm version 0.2.0 --no-git-tag-version
377
+ git commit -am "Version: 0.2.0"
378
+
379
+ # 2. Push dev and open a merge request dev -> main on GitLab, then merge it
380
+ git push origin dev
381
+
382
+ # 3. Tag the merged main (annotated) and push the tag
383
+ git fetch origin
384
+ git tag -a v0.2.0 origin/main -m "Release 0.2.0"
385
+ git push origin v0.2.0
386
+
387
+ # 4. Build and publish to npm — see "Publishing" above
156
388
  ```
157
389
 
158
- ```sh
159
- $ npm install
160
- $ npm run build # Compile the project into the ./dist folder.
161
- $ npm run dev # Work on the project
162
- $ npm run test # Vtest
163
- $ npm run lint
164
- $ npm run format
165
- $ npm install # before publishing the package (generation of the package-lock.json file)
166
- $ npm login
167
- $ npm publish --access public
168
- $ npm info @popovandrii/ui-elements@0.0.x
169
- $ npm install @popovandrii/ui-elements@latest
170
- ```
171
-
172
- The `./playground` folder for viewing in a browser
390
+ On GitLab, create the release from **Deploy → Releases → New release**, pick the
391
+ `vX.Y.Z` tag and paste the changelog as the release notes.
392
+
393
+ ## License
394
+
395
+ [MIT](LICENSE) © Andrii Popov
package/dist/Button.d.ts CHANGED
@@ -1,19 +1,31 @@
1
1
  interface SelectorMap {
2
2
  main: string;
3
3
  }
4
+ interface InitOptions {
5
+ /** Limit scanning to this subtree instead of the whole document. */
6
+ root?: Document | HTMLElement;
7
+ /** Auto re-scan on DOM changes inside the root (debounced MutationObserver). */
8
+ observe?: boolean;
9
+ }
4
10
  export declare class Button {
5
- #private;
6
11
  private selectors;
7
12
  private buttons;
8
13
  private abortController;
9
- private dbug;
10
- constructor(selectors?: Partial<SelectorMap>, dbug?: boolean);
14
+ private debug;
15
+ private root;
16
+ private observer;
17
+ private rescanTimer;
18
+ constructor(selectors?: Partial<SelectorMap>, debug?: boolean, options?: InitOptions);
19
+ /** Watch the root for added/removed nodes and re-scan (debounced). */
20
+ private observe;
11
21
  setValue(el: HTMLElement, value: string, label?: string): void;
12
22
  destroy(): void;
13
23
  private disableEl;
14
24
  private enableEl;
15
25
  private isDisabled;
16
26
  setDisabled(el: HTMLElement, disabled: boolean): void;
27
+ /** Bind every not-yet-bound `.UIb`. Safe to call repeatedly (SPA re-render). */
28
+ scan(): void;
17
29
  private ripple;
18
30
  private customEvent;
19
31
  }
@@ -3,16 +3,28 @@ interface SelectorMap {
3
3
  btn: string;
4
4
  input: string;
5
5
  }
6
+ interface InitOptions {
7
+ /** Limit scanning to this subtree instead of the whole document. */
8
+ root?: Document | HTMLElement;
9
+ /** Auto re-scan on DOM changes inside the root (debounced MutationObserver). */
10
+ observe?: boolean;
11
+ }
6
12
  export declare class ButtonGroup {
7
- #private;
8
13
  private selectors;
9
14
  private main;
10
15
  private abortController;
11
- private dbug;
12
- constructor(selectors?: Partial<SelectorMap>, dbug?: boolean);
16
+ private debug;
17
+ private root;
18
+ private observer;
19
+ private rescanTimer;
20
+ constructor(selectors?: Partial<SelectorMap>, debug?: boolean, options?: InitOptions);
21
+ /** Watch the root for added/removed nodes and re-scan (debounced). */
22
+ private observe;
13
23
  destroy(): void;
14
- private disableEl;
15
24
  setValue(el: HTMLElement, value: string): void;
16
- private costomEvent;
25
+ /** Bind every not-yet-bound `.UIbg`. Safe to call repeatedly (SPA re-render). */
26
+ scan(): void;
27
+ private ripple;
28
+ private customEvent;
17
29
  }
18
30
  export {};
package/dist/Select.d.ts CHANGED
@@ -9,17 +9,31 @@ interface SelectorMap {
9
9
  flash: string;
10
10
  excludedItems: Array<string>;
11
11
  }
12
+ interface InitOptions {
13
+ /** Limit scanning to this subtree instead of the whole document. */
14
+ root?: Document | HTMLElement;
15
+ /** Auto re-scan on DOM changes inside the root (debounced MutationObserver). */
16
+ observe?: boolean;
17
+ }
12
18
  export declare class Select {
13
19
  private selectors;
14
20
  private main;
15
21
  private itemArrowInitialized;
16
22
  private abortController;
17
- private dbug;
23
+ private globalAbortController;
24
+ private debug;
25
+ private root;
26
+ private observer;
27
+ private rescanTimer;
18
28
  private selectMap;
19
- constructor(selectors?: Partial<SelectorMap>, dbug?: boolean);
29
+ constructor(selectors?: Partial<SelectorMap>, debug?: boolean, options?: InitOptions);
30
+ /** Watch the root for added/removed nodes and re-scan (debounced). */
31
+ private observe;
20
32
  private filterExcluded;
21
33
  private filterSearch;
22
34
  destroy(): void;
35
+ /** Full teardown: destroy() plus the lifetime-scoped global listener. */
36
+ dispose(): void;
23
37
  private disableEl;
24
38
  /**
25
39
  *
@@ -28,7 +42,8 @@ export declare class Select {
28
42
  * @returns
29
43
  */
30
44
  setValue(el: HTMLElement, value: string | number): void;
31
- private event;
45
+ /** Bind every not-yet-bound `.UIselect`. Safe to call repeatedly (SPA re-render). */
46
+ scan(): void;
32
47
  private itemArrow;
33
48
  private itemsPosition;
34
49
  private items;
package/dist/SpinBox.d.ts CHANGED
@@ -4,19 +4,31 @@ interface SelectorMap {
4
4
  input: string;
5
5
  disabledBtn: string;
6
6
  }
7
+ interface InitOptions {
8
+ /** Limit scanning to this subtree instead of the whole document. */
9
+ root?: Document | HTMLElement;
10
+ /** Auto re-scan on DOM changes inside the root (debounced MutationObserver). */
11
+ observe?: boolean;
12
+ }
7
13
  export declare class SpinBox {
8
14
  private selectors;
9
15
  private spinBoxes;
10
16
  private abortController;
11
- private dbug;
12
- constructor(selectors?: Partial<SelectorMap>, dbug?: boolean);
17
+ private debug;
18
+ private root;
19
+ private observer;
20
+ private rescanTimer;
21
+ constructor(selectors?: Partial<SelectorMap>, debug?: boolean, options?: InitOptions);
22
+ /** Watch the root for added/removed nodes and re-scan (debounced). */
23
+ private observe;
13
24
  private state;
14
25
  destroy(): void;
15
26
  private disableEl;
16
27
  setValue(el: HTMLElement, value: string | number): void;
17
28
  getValidDataNumber: (el: HTMLElement, attr: string) => number;
18
- private event;
29
+ /** Bind every not-yet-bound `.UIsp`. Safe to call repeatedly (SPA re-render). */
30
+ scan(): void;
19
31
  private ripple;
20
- private costomEvent;
32
+ private customEvent;
21
33
  }
22
34
  export {};