@dodlhuat/basix 1.3.5 → 1.4.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 (82) hide show
  1. package/README.md +83 -7
  2. package/css/checkbox.scss +15 -2
  3. package/css/color-picker.scss +278 -0
  4. package/css/datepicker.scss +5 -3
  5. package/css/push-menu.scss +7 -0
  6. package/css/stepper.scss +1 -1
  7. package/css/style.css +291 -6
  8. package/css/style.css.map +1 -1
  9. package/css/style.min.css +1 -1
  10. package/css/style.min.css.map +1 -1
  11. package/css/style.scss +1 -0
  12. package/css/table.scss +33 -0
  13. package/js/bottom-sheet.d.ts +0 -2
  14. package/js/bottom-sheet.js +0 -2
  15. package/js/calendar.d.ts +9 -17
  16. package/js/calendar.js +4 -7
  17. package/js/carousel.d.ts +0 -2
  18. package/js/carousel.js +1 -3
  19. package/js/chart.d.ts +6 -14
  20. package/js/chart.js +3 -5
  21. package/js/code-viewer.d.ts +2 -1
  22. package/js/code-viewer.js +8 -2
  23. package/js/color-picker.d.ts +50 -0
  24. package/js/color-picker.js +291 -0
  25. package/js/context-menu.d.ts +0 -3
  26. package/js/context-menu.js +0 -3
  27. package/js/datepicker.d.ts +0 -4
  28. package/js/datepicker.js +0 -1
  29. package/js/docs-nav.js +1 -0
  30. package/js/dropdown.d.ts +1 -4
  31. package/js/dropdown.js +0 -1
  32. package/js/editor.d.ts +0 -4
  33. package/js/editor.js +3 -5
  34. package/js/file-uploader.d.ts +0 -4
  35. package/js/file-uploader.js +0 -1
  36. package/js/flyout-menu.d.ts +0 -2
  37. package/js/flyout-menu.js +0 -1
  38. package/js/gallery.d.ts +1 -4
  39. package/js/gallery.js +18 -23
  40. package/js/group-picker.d.ts +0 -5
  41. package/js/group-picker.js +6 -8
  42. package/js/lightbox.d.ts +1 -4
  43. package/js/lightbox.js +6 -10
  44. package/js/modal.d.ts +0 -2
  45. package/js/modal.js +0 -1
  46. package/js/popover.d.ts +0 -3
  47. package/js/popover.js +0 -2
  48. package/js/position.d.ts +0 -13
  49. package/js/position.js +0 -12
  50. package/js/push-menu.d.ts +0 -3
  51. package/js/push-menu.js +6 -11
  52. package/js/range-slider.d.ts +0 -1
  53. package/js/range-slider.js +2 -3
  54. package/js/scroll.d.ts +0 -2
  55. package/js/scroll.js +5 -6
  56. package/js/scrollbar.d.ts +2 -10
  57. package/js/scrollbar.js +25 -42
  58. package/js/select.d.ts +2 -3
  59. package/js/select.js +6 -9
  60. package/js/sidebar-nav.d.ts +4 -15
  61. package/js/sidebar-nav.js +21 -35
  62. package/js/stepper.d.ts +5 -2
  63. package/js/stepper.js +42 -6
  64. package/js/table.d.ts +1 -56
  65. package/js/table.js +4 -56
  66. package/js/tabs.d.ts +1 -32
  67. package/js/tabs.js +7 -39
  68. package/js/theme.d.ts +0 -44
  69. package/js/theme.js +0 -44
  70. package/js/timepicker.d.ts +1 -4
  71. package/js/timepicker.js +1 -2
  72. package/js/toast.d.ts +0 -2
  73. package/js/toast.js +0 -1
  74. package/js/tooltip.d.ts +0 -4
  75. package/js/tooltip.js +0 -1
  76. package/js/tree.d.ts +0 -3
  77. package/js/tree.js +9 -11
  78. package/js/utils.d.ts +0 -10
  79. package/js/utils.js +2 -42
  80. package/js/virtual-dropdown.d.ts +3 -5
  81. package/js/virtual-dropdown.js +17 -66
  82. package/package.json +1 -1
package/README.md CHANGED
@@ -1,10 +1,38 @@
1
- # Basix 1.3.5
1
+ # Basix 1.4.0
2
2
 
3
3
  Basix is intended as a starter for the rapid development of a design. Each design element can be added individually to
4
4
  include only the data required. It is using plain javascript / typescript and therefore is not dependent on any plugin.
5
5
 
6
6
  A demo can be found here: <a href="http://www.andibauer.at/basix/" target="_blank">http://www.andibauer.at/basix/</a>
7
7
 
8
+ ---
9
+
10
+ ## Migration Guide
11
+
12
+ ### 1.3.5 → 1.4.0
13
+
14
+ #### Breaking changes
15
+
16
+ **`TimeSpanPicker` — option `toString` renamed to `toLabel`**
17
+
18
+ The `toString` option shadowed `Object.prototype.toString` and is now renamed to `toLabel`. Update any call sites that pass this option:
19
+
20
+ ```js
21
+ // Before (1.3.5)
22
+ new TimeSpanPicker('container', { toString: 'Bis' });
23
+
24
+ // After (1.4.0)
25
+ new TimeSpanPicker('container', { toLabel: 'Bis' });
26
+ ```
27
+
28
+ #### Bug fixes
29
+
30
+ - **`MasonryGallery`** — the `reload` option was previously hard-coded to `2` and ignored. It now respects the value you pass. The default remains `2`, so existing usage without the option is unaffected.
31
+ - **`Tabs`** — `destroy()` no longer operates on detached DOM nodes. Event listeners are now cleaned up via `AbortController`.
32
+ - **`gallery`** — image-load errors no longer cause an unhandled promise rejection; they are logged to `console.error` instead.
33
+
34
+ ---
35
+
8
36
  ### Benefits
9
37
 
10
38
  * lightweight
@@ -479,8 +507,19 @@ The Tabs component creates accessible tabbed interfaces. Supports horizontal/ver
479
507
 
480
508
  | Option | Type | Default | Description |
481
509
  |---|---|---|---|
482
- | `layout` | string | `'horizontal'` | Layout of the tabs, either `'horizontal'` or `'vertical'` |
483
- | `defaultTab` | integer | `0` | Index of the default active tab (0-based) |
510
+ | `layout` | string | `'horizontal'` | `'horizontal'` or `'vertical'` |
511
+ | `defaultTab` | number | `0` | Index of the initially active tab (0-based) |
512
+ | `menuPos` | string | `'top'` / `'left'` | Tab bar position: `'top'`, `'bottom'`, `'left'`, `'right'`. Defaults to `'top'` for horizontal layout, `'left'` for vertical. |
513
+ | `onChange` | function | — | Callback fired on tab change: `(index: number) => void` |
514
+
515
+ | Method | Description |
516
+ |---|---|
517
+ | `goToTab(index)` | Programmatically activate a tab and focus it |
518
+ | `getCurrentTab()` | Returns the index of the currently active tab |
519
+ | `getTabCount()` | Returns the total number of tabs |
520
+ | `enableTab(index)` | Re-enables a disabled tab |
521
+ | `disableTab(index)` | Disables a tab; if it was active, moves focus to the next enabled tab |
522
+ | `destroy()` | Removes event listeners and cleans up ARIA attributes |
484
523
 
485
524
  ### Timeline
486
525
 
@@ -788,7 +827,7 @@ const picker = new TimeSpanPicker('my-container', {
788
827
  defaultStart: '09:00',
789
828
  defaultEnd: '17:00',
790
829
  fromString: 'Von', // optional — defaults to 'From'
791
- toString: 'Bis', // optional — defaults to 'To'
830
+ toLabel: 'Bis', // optional — defaults to 'To'
792
831
  onChange: (start, end) => console.log(start, end),
793
832
  });
794
833
 
@@ -799,6 +838,36 @@ picker.isValid(); // boolean
799
838
  picker.destroy();
800
839
  ```
801
840
 
841
+ ### Color Picker
842
+
843
+ The ColorPicker component renders a canvas-based HSB color picker with a hue slider and hex/RGB inputs. It builds its own DOM inside any container element and is fully mobile-friendly — drag gestures on the color field do not trigger page scroll.
844
+
845
+ ``` js
846
+ import { ColorPicker } from '@dodlhuat/basix/js/color-picker.js';
847
+
848
+ const picker = new ColorPicker('#my-picker', {
849
+ value: '#3d63dd',
850
+ onChange: (hex, rgb) => {
851
+ console.log(hex); // '#3d63dd'
852
+ console.log(rgb.r, rgb.g, rgb.b); // 61, 99, 221
853
+ },
854
+ });
855
+
856
+ picker.getValue(); // current hex string, e.g. '#3d63dd'
857
+ picker.setValue('#ff6b6b'); // set color programmatically
858
+ picker.destroy(); // removes listeners, disconnects ResizeObserver, clears DOM
859
+ ```
860
+
861
+ ``` html
862
+ <!-- Any empty container works — the component builds its own markup inside -->
863
+ <div id="my-picker"></div>
864
+ ```
865
+
866
+ | Option | Type | Default | Description |
867
+ |---|---|---|---|
868
+ | `value` | string | `'#ff0000'` | Initial hex color (e.g. `'#3d63dd'`). Must be a 6-digit hex string. |
869
+ | `onChange` | function | — | Called on every color change: `(hex: string, rgb: { r, g, b }) => void` |
870
+
802
871
  ### Select
803
872
 
804
873
  The Select component wraps a native `<select>` element with custom Basix styling. Supports single and multi-select.
@@ -813,9 +882,11 @@ The Select component wraps a native `<select>` element with custom Basix styling
813
882
  ``` js
814
883
  const sel = new Select('#my-select');
815
884
  sel.value(); // returns selected value string, or string[] for multi-select
885
+ sel.destroy(); // removes the document click listener
816
886
 
817
- // Or initialise by passing the element directly
818
- Select.init(document.querySelector('#my-select'));
887
+ // Static convenience form returns a cleanup function to remove the document listener
888
+ const cleanup = Select.init(document.querySelector('#my-select'));
889
+ cleanup?.(); // call when the select is removed from the DOM
819
890
  ```
820
891
 
821
892
  ### Code Viewer
@@ -823,9 +894,11 @@ Select.init(document.querySelector('#my-select'));
823
894
  The CodeViewer component renders syntax-highlighted code blocks inside any container. Supports JavaScript, HTML, and CSS.
824
895
 
825
896
  ``` js
826
- new CodeViewer('#output', '<div class="card">Hello</div>', 'html');
897
+ const viewer = new CodeViewer('#output', '<div class="card">Hello</div>', 'html');
827
898
  new CodeViewer('#output', 'const x = 42;', 'javascript');
828
899
  new CodeViewer('#output', '.card { padding: 1rem; }', 'css');
900
+
901
+ viewer.destroy(); // removes event listeners and clears the container
829
902
  ```
830
903
 
831
904
  ### Editor
@@ -914,6 +987,9 @@ editor1.destroy();
914
987
  | `clear` | Clears all content |
915
988
  | `clean` | Strips formatting from the current selection |
916
989
  | `toggle-code` | Shows / hides the HTML side panel |
990
+ | `apply-code` | Applies the HTML source panel content to the editable area |
991
+ | `sanitize-code` | Sanitizes the HTML source, then applies it to the editable area |
992
+ | `minify-code` | Minifies the HTML source (removes whitespace and newlines) |
917
993
 
918
994
  ### Custom Scrollbar
919
995
 
package/css/checkbox.scss CHANGED
@@ -6,6 +6,13 @@
6
6
  position: absolute;
7
7
  opacity: 0;
8
8
  width: auto;
9
+ appearance: none;
10
+ -webkit-appearance: none;
11
+ border: none;
12
+ box-shadow: none;
13
+ min-height: 0;
14
+ padding: 0;
15
+ background: transparent;
9
16
 
10
17
  & + label {
11
18
  position: relative;
@@ -29,7 +36,7 @@
29
36
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
30
37
  }
31
38
 
32
- &:hover + label::before {
39
+ &:not(:disabled):hover + label::before {
33
40
  border-color: var(--accent-color);
34
41
  }
35
42
 
@@ -58,11 +65,12 @@
58
65
  &:disabled + label {
59
66
  color: var(--secondary-text);
60
67
  cursor: not-allowed;
68
+ pointer-events: none;
61
69
  }
62
70
 
63
71
  &:disabled + label::before {
64
72
  background-color: var(--secondary-background);
65
- border-color: var(--disabled);
73
+ border-color: color-mix(in srgb, var(--secondary-text) 35%, transparent);
66
74
  box-shadow: none;
67
75
  }
68
76
 
@@ -70,4 +78,9 @@
70
78
  background-color: var(--disabled);
71
79
  border-color: var(--disabled);
72
80
  }
81
+
82
+ &:disabled:checked + label::after {
83
+ border-right-color: color-mix(in srgb, var(--primary-text) 40%, transparent);
84
+ border-bottom-color: color-mix(in srgb, var(--primary-text) 40%, transparent);
85
+ }
73
86
  }
@@ -0,0 +1,278 @@
1
+ @use "properties";
2
+ @use "parameters" as *;
3
+
4
+ $thumb-size: 18px;
5
+ $thumb-size-touch: 22px;
6
+ $track-height: 12px;
7
+ $cursor-size: 14px;
8
+ $preview-size: 40px;
9
+
10
+ .color-picker {
11
+ width: 100%;
12
+ background: var(--background);
13
+ border: 1px solid var(--divider);
14
+ border-radius: $radius-lg;
15
+ padding: $spacing;
16
+ box-sizing: border-box;
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: calc($spacing * 0.75);
20
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
21
+ }
22
+
23
+ // ─── Saturation / brightness field ────────────────────────────────────────────
24
+
25
+ .color-picker__field {
26
+ position: relative;
27
+ overflow: hidden;
28
+ height: 180px;
29
+ border-radius: $border-radius;
30
+ cursor: crosshair;
31
+ touch-action: none;
32
+ flex-shrink: 0;
33
+
34
+ @media (min-width: 380px) {
35
+ height: 200px;
36
+ }
37
+ }
38
+
39
+ .color-picker__canvas {
40
+ display: block;
41
+ width: 100%;
42
+ height: 100%;
43
+ }
44
+
45
+ .color-picker__cursor {
46
+ position: absolute;
47
+ width: $cursor-size;
48
+ height: $cursor-size;
49
+ border-radius: 50%;
50
+ border: 2px solid #fff;
51
+ box-shadow:
52
+ 0 0 0 1px rgba(0, 0, 0, 0.3),
53
+ 0 1px 4px rgba(0, 0, 0, 0.4);
54
+ transform: translate(-50%, -50%);
55
+ pointer-events: none;
56
+ transition: none;
57
+
58
+ &--dark {
59
+ border-color: rgba(0, 0, 0, 0.6);
60
+ }
61
+ }
62
+
63
+ // ─── Controls row: hue slider + preview ───────────────────────────────────────
64
+
65
+ .color-picker__controls {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: $spacing;
69
+ }
70
+
71
+ // Rainbow strip is the track div itself — input sits absolutely centred over it
72
+ // so the input never contributes to layout height.
73
+ .color-picker__hue-track {
74
+ flex: 1;
75
+ position: relative;
76
+ height: $track-height;
77
+ border-radius: calc($track-height / 2);
78
+ background: linear-gradient(
79
+ to right,
80
+ #f00 0%,
81
+ #ff0 17%,
82
+ #0f0 33%,
83
+ #0ff 50%,
84
+ #00f 67%,
85
+ #f0f 83%,
86
+ #f00 100%
87
+ );
88
+ }
89
+
90
+ .color-picker__hue-slider {
91
+ -webkit-appearance: none;
92
+ appearance: none;
93
+ position: absolute;
94
+ top: 50%;
95
+ left: 0;
96
+ transform: translateY(-50%);
97
+ width: 100%;
98
+ height: $thumb-size;
99
+ background: transparent;
100
+ cursor: pointer;
101
+ margin: 0;
102
+ padding: 0;
103
+ border: none;
104
+ outline: none;
105
+
106
+ &::-webkit-slider-runnable-track {
107
+ height: $thumb-size;
108
+ background: transparent;
109
+ }
110
+
111
+ &::-webkit-slider-thumb {
112
+ -webkit-appearance: none;
113
+ appearance: none;
114
+ width: $thumb-size;
115
+ height: $thumb-size;
116
+ border-radius: 50%;
117
+ background: #fff;
118
+ border: 2px solid #fff;
119
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
120
+ margin-top: 0;
121
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
122
+ }
123
+
124
+ &::-moz-range-track {
125
+ height: $thumb-size;
126
+ background: transparent;
127
+ }
128
+
129
+ // ── Moz thumb ─────────────────────────────────────────────────────────────
130
+ &::-moz-range-thumb {
131
+ width: $thumb-size;
132
+ height: $thumb-size;
133
+ border-radius: 50%;
134
+ background: #fff;
135
+ border: 2px solid #fff;
136
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
137
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
138
+ cursor: pointer;
139
+ }
140
+
141
+ &:focus {
142
+ outline: none;
143
+ }
144
+
145
+ &:focus-visible::-webkit-slider-thumb {
146
+ box-shadow:
147
+ 0 1px 3px rgba(0, 0, 0, 0.35),
148
+ 0 0 0 3px var(--accent-color-tint);
149
+ }
150
+
151
+ &:focus-visible::-moz-range-thumb {
152
+ box-shadow:
153
+ 0 1px 3px rgba(0, 0, 0, 0.35),
154
+ 0 0 0 3px var(--accent-color-tint);
155
+ }
156
+
157
+ &:hover::-webkit-slider-thumb {
158
+ transform: scale(1.15);
159
+ }
160
+
161
+ &:hover::-moz-range-thumb {
162
+ transform: scale(1.15);
163
+ }
164
+
165
+ &:active::-webkit-slider-thumb {
166
+ transform: scale(1.2);
167
+ }
168
+
169
+ &:active::-moz-range-thumb {
170
+ transform: scale(1.2);
171
+ }
172
+ }
173
+
174
+ .color-picker__preview {
175
+ width: $preview-size;
176
+ height: $preview-size;
177
+ border-radius: 50%;
178
+ border: 1px solid var(--divider);
179
+ flex-shrink: 0;
180
+ }
181
+
182
+ // ─── Text inputs ──────────────────────────────────────────────────────────────
183
+
184
+ .color-picker__inputs {
185
+ display: flex;
186
+ gap: calc($spacing * 0.5);
187
+ }
188
+
189
+ .color-picker__input-group {
190
+ display: flex;
191
+ flex-direction: column;
192
+ gap: 4px;
193
+ flex: 1;
194
+
195
+ &--hex {
196
+ flex: 2;
197
+ }
198
+
199
+ label {
200
+ font-size: 0.7rem;
201
+ font-weight: 500;
202
+ color: var(--secondary-text);
203
+ text-align: center;
204
+ line-height: 1;
205
+ user-select: none;
206
+ }
207
+ }
208
+
209
+ .color-picker__hex,
210
+ .color-picker__r,
211
+ .color-picker__g,
212
+ .color-picker__b {
213
+ width: 100%;
214
+ box-sizing: border-box;
215
+ padding: 0.35rem 0.4rem;
216
+ border: 1px solid var(--divider);
217
+ border-radius: $border-radius;
218
+ font-family: monospace;
219
+ font-size: 0.8rem;
220
+ text-align: center;
221
+ background: var(--secondary-background);
222
+ color: var(--primary-text);
223
+ transition: border-color 0.15s, box-shadow 0.15s;
224
+ outline: none;
225
+ -moz-appearance: textfield;
226
+
227
+ &::-webkit-inner-spin-button,
228
+ &::-webkit-outer-spin-button {
229
+ -webkit-appearance: none;
230
+ margin: 0;
231
+ }
232
+
233
+ &:focus {
234
+ border-color: var(--accent-color);
235
+ box-shadow: 0 0 0 2px var(--accent-color-tint);
236
+ }
237
+ }
238
+
239
+ // ─── Mobile ───────────────────────────────────────────────────────────────────
240
+
241
+ @media (max-width: 480px) {
242
+ .color-picker__field {
243
+ height: 160px;
244
+ }
245
+
246
+ .color-picker__hue-slider {
247
+ height: $thumb-size-touch;
248
+
249
+ &::-webkit-slider-runnable-track { height: $thumb-size-touch; }
250
+ &::-webkit-slider-thumb { width: $thumb-size-touch; height: $thumb-size-touch; }
251
+ &::-moz-range-track { height: $thumb-size-touch; }
252
+ &::-moz-range-thumb { width: $thumb-size-touch; height: $thumb-size-touch; }
253
+ }
254
+
255
+ .color-picker__hex,
256
+ .color-picker__r,
257
+ .color-picker__g,
258
+ .color-picker__b {
259
+ font-size: 0.75rem;
260
+ padding: 0.28rem 0.3rem;
261
+ }
262
+ }
263
+
264
+ @media (prefers-reduced-motion: reduce) {
265
+ .color-picker__hue-slider {
266
+ &::-webkit-slider-thumb,
267
+ &::-moz-range-thumb {
268
+ transition: none;
269
+ }
270
+ }
271
+
272
+ .color-picker__hex,
273
+ .color-picker__r,
274
+ .color-picker__g,
275
+ .color-picker__b {
276
+ transition: none;
277
+ }
278
+ }
@@ -104,8 +104,8 @@
104
104
  }
105
105
 
106
106
  .datepicker-day.in-range {
107
- background-color: var(--accent-color-lighten);
108
- color: var(--accent-color-text);
107
+ background-color: color-mix(in srgb, var(--accent-color) 15%, transparent);
108
+ color: var(--primary-text);
109
109
  border-radius: 0;
110
110
  }
111
111
 
@@ -128,7 +128,9 @@
128
128
  color: var(--accent-color);
129
129
  }
130
130
 
131
- .datepicker-day.selected.today {
131
+ .datepicker-day.selected.today,
132
+ .datepicker-day.range-start.today,
133
+ .datepicker-day.range-end.today {
132
134
  color: var(--accent-color-text);
133
135
  }
134
136
 
@@ -128,6 +128,13 @@ nav.push-menu {
128
128
 
129
129
  nav li {
130
130
  margin: 0;
131
+ list-style: none;
132
+ }
133
+
134
+ nav ul {
135
+ list-style: none;
136
+ padding: 0;
137
+ margin: 0;
131
138
  }
132
139
 
133
140
  nav a,
package/css/stepper.scss CHANGED
@@ -118,7 +118,7 @@ $connector-thickness: 2px;
118
118
  border-color: var(--accent-color);
119
119
  background: color-mix(in srgb, var(--accent-color) 10%, var(--background));
120
120
  color: var(--accent-color);
121
- animation: stepper-pulse 2.4s ease-out infinite;
121
+ animation: stepper-pulse 2.4s ease-out 3;
122
122
  }
123
123
 
124
124
  .stepper-title {