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