@serkonda7/solid-components 0.3.2 → 0.4.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.
@@ -0,0 +1,39 @@
1
+ import type { JSX } from 'solid-js';
2
+ export interface ModalProps {
3
+ /** Whether the modal is shown. The content is mounted only while open. */
4
+ open: boolean;
5
+ /** Called on Escape, a backdrop click or the close button; set `open` to false here. */
6
+ onClose: () => void;
7
+ /**
8
+ * Called before Escape, a backdrop click or the close button call `onClose`;
9
+ * return `false` (or resolve to it) to keep the modal open. Further requests
10
+ * are ignored while a returned promise is pending.
11
+ */
12
+ onBeforeClose?: () => boolean | Promise<boolean>;
13
+ /** Heading in the modal header; also its accessible name. */
14
+ title?: JSX.Element;
15
+ /** Accessible name when there is no `title`. */
16
+ ariaLabel?: string;
17
+ /** Id of an element describing the modal, for `aria-describedby`. */
18
+ describedBy?: string;
19
+ /** Rendered below the body, e.g. action buttons. */
20
+ footer?: JSX.Element;
21
+ /** Accessible name of the close button. Defaults to `Close`. */
22
+ closeLabel?: string;
23
+ /** Hides the header close button. Escape and the backdrop still close. */
24
+ hideCloseButton?: boolean;
25
+ /** Defaults to `true`. */
26
+ closeOnEscape?: boolean;
27
+ /** Close on a click on the backdrop. Defaults to `true`. */
28
+ closeOnBackdrop?: boolean;
29
+ class?: string;
30
+ children?: JSX.Element;
31
+ }
32
+ /**
33
+ * Modal dialog on top of a native `<dialog>` opened with `showModal()`: it sits
34
+ * in the top layer and the rest of the page is inert, so focus stays inside.
35
+ * Opening focuses the first `autofocus` element, else the first focusable one;
36
+ * closing returns focus to the element that had it before. Controlled through
37
+ * `open`/`onClose`.
38
+ */
39
+ export declare function Modal(props: ModalProps): JSX.Element;
package/dist/styles.css CHANGED
@@ -91,9 +91,21 @@
91
91
  }
92
92
 
93
93
  .data-table-customizer-grip {
94
+ display: inline-flex;
94
95
  flex: none;
95
96
  opacity: 0.5;
96
97
  cursor: grab;
98
+ touch-action: none;
99
+ }
100
+
101
+ .data-table-customizer-grip:focus-visible {
102
+ opacity: 1;
103
+ }
104
+
105
+ @media (pointer: coarse) {
106
+ .data-table-customizer-grip {
107
+ padding: 0.5rem;
108
+ }
97
109
  }
98
110
 
99
111
  .data-table-customizer-option {
@@ -121,6 +133,13 @@
121
133
  border-collapse: collapse;
122
134
  }
123
135
 
136
+ .data-table tbody tr:hover {
137
+ background: var(
138
+ --data-table-row-hover-bg,
139
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
140
+ );
141
+ }
142
+
124
143
  .data-table-resized {
125
144
  table-layout: fixed;
126
145
  max-width: none;
@@ -174,6 +193,65 @@
174
193
  outline: none;
175
194
  }
176
195
 
196
+ /* Sticky cells need opaque backgrounds and their own edge: collapsed borders
197
+ scroll away with the table. */
198
+ .data-table-scroll:has(> .data-table-sticky-header) {
199
+ max-height: var(--data-table-max-height, 80dvh);
200
+ }
201
+
202
+ .data-table.data-table-sticky-header th {
203
+ position: sticky;
204
+ z-index: 2;
205
+ top: 0;
206
+ background: var(
207
+ --data-table-header-bg,
208
+ var(--surface, light-dark(#ffffff, #1d2024))
209
+ );
210
+ box-shadow: inset 0 -1px 0
211
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
212
+ }
213
+
214
+ /* A sticky header cell is painted over the part that overhangs its neighbour. */
215
+ .data-table-sticky-header .data-table-resize-handle {
216
+ right: 0;
217
+ }
218
+
219
+ .data-table.data-table-sticky-actions .data-table-actions {
220
+ position: sticky;
221
+ z-index: 1;
222
+ right: 0;
223
+ background: var(
224
+ --data-table-bg,
225
+ var(--surface, light-dark(#ffffff, #1d2024))
226
+ );
227
+ box-shadow: inset 1px 0 0
228
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
229
+ }
230
+
231
+ .data-table.data-table-sticky-actions tbody tr:hover .data-table-actions {
232
+ background: var(
233
+ --data-table-row-hover-bg,
234
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
235
+ );
236
+ }
237
+
238
+ .data-table.data-table-sticky-actions th.data-table-actions {
239
+ z-index: 3;
240
+ background: var(
241
+ --data-table-header-bg,
242
+ var(--surface, light-dark(#ffffff, #1d2024))
243
+ );
244
+ }
245
+
246
+ .data-table.data-table-sticky-header.data-table-sticky-actions
247
+ th.data-table-actions {
248
+ box-shadow:
249
+ inset 1px 0 0
250
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #3a3e45))),
251
+ inset 0 -1px 0
252
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
253
+ }
254
+
177
255
  .data-table-sort {
178
256
  padding: 0;
179
257
  border: none;
@@ -199,10 +277,103 @@
199
277
  text-align: center;
200
278
  }
201
279
 
280
+ .data-table-sort-indicator:empty::before {
281
+ content: "↕";
282
+ opacity: 0.4;
283
+ }
284
+
285
+ .data-table-sort:hover .data-table-sort-indicator:empty::before {
286
+ opacity: 0.8;
287
+ }
288
+
202
289
  .data-table-status {
203
290
  color: var(--text-secondary, #616161);
204
291
  }
205
292
 
293
+ .data-table-visually-hidden {
294
+ position: absolute;
295
+ overflow: hidden;
296
+ width: 1px;
297
+ height: 1px;
298
+ clip-path: inset(50%);
299
+ white-space: nowrap;
300
+ }
301
+
302
+ .data-table-skeleton {
303
+ display: block;
304
+ width: 70%;
305
+ min-width: 2.5rem;
306
+ height: 1em;
307
+ border-radius: 0.25rem;
308
+ background: var(
309
+ --data-table-skeleton-bg,
310
+ var(--border, light-dark(#e4e4e4, #30343a))
311
+ );
312
+ animation: data-table-skeleton-pulse 1.4s ease-in-out infinite;
313
+ }
314
+
315
+ @keyframes data-table-skeleton-pulse {
316
+ 50% {
317
+ opacity: 0.45;
318
+ }
319
+ }
320
+
321
+ @media (prefers-reduced-motion: reduce) {
322
+ .data-table-skeleton {
323
+ animation: none;
324
+ }
325
+ }
326
+
327
+ .data-table-pagination {
328
+ display: flex;
329
+ flex-wrap: wrap;
330
+ align-items: center;
331
+ justify-content: flex-end;
332
+ gap: 0.5rem 0.75rem;
333
+ padding: 0.5rem 0;
334
+ }
335
+
336
+ .data-table-page-size {
337
+ display: inline-flex;
338
+ align-items: center;
339
+ gap: 0.375rem;
340
+ }
341
+
342
+ .data-table-pagination select,
343
+ .data-table-pagination button {
344
+ border: 1px solid
345
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
346
+ border-radius: 0.375rem;
347
+ background: var(
348
+ --data-table-customizer-bg,
349
+ var(--surface, light-dark(#ffffff, #1d2024))
350
+ );
351
+ color: inherit;
352
+ font: inherit;
353
+ }
354
+
355
+ .data-table-pagination select {
356
+ padding: 0.25rem;
357
+ }
358
+
359
+ .data-table-pagination button {
360
+ display: inline-flex;
361
+ padding: 0.25rem;
362
+ cursor: pointer;
363
+ }
364
+
365
+ .data-table-pagination button:hover:enabled {
366
+ background: var(
367
+ --data-table-customizer-hover-bg,
368
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
369
+ );
370
+ }
371
+
372
+ .data-table-pagination button:disabled {
373
+ opacity: 0.4;
374
+ cursor: not-allowed;
375
+ }
376
+
206
377
  /* Combobox: text input plus an absolutely positioned listbox below it. */
207
378
  .combobox {
208
379
  position: relative;
@@ -320,6 +491,15 @@
320
491
  pointer-events: none;
321
492
  }
322
493
 
494
+ /* `previewAtCursor`: float at the mouse, shifted back into the viewport. */
495
+ .combobox-preview-cursor {
496
+ position: fixed;
497
+ top: calc(var(--combobox-cursor-y) + 1rem);
498
+ left: calc(var(--combobox-cursor-x) + 1rem);
499
+ translate: min(0px, calc(100vw - var(--combobox-cursor-x) - 1.5rem - 100%))
500
+ min(0px, calc(100vh - var(--combobox-cursor-y) - 1.5rem - 100%));
501
+ }
502
+
323
503
  .combobox-option,
324
504
  .combobox-empty {
325
505
  padding: 0.3rem 0.5rem;
@@ -642,6 +822,16 @@
642
822
  min-width: 0;
643
823
  }
644
824
 
825
+ .filter-table-range {
826
+ display: flex;
827
+ gap: 0.5rem;
828
+ }
829
+
830
+ .filter-table-range .filter-table-input {
831
+ flex: 1;
832
+ min-width: 0;
833
+ }
834
+
645
835
  .filter-table-form {
646
836
  /* Label column width and gap; group headings are indented by both so they
647
837
  line up with the inputs. */
@@ -749,6 +939,67 @@
749
939
  }
750
940
  }
751
941
 
942
+ /* ColorInput: hex input with a native color picker as its leading swatch. */
943
+ .color-input {
944
+ position: relative;
945
+ display: flex;
946
+ align-items: center;
947
+ width: 9rem;
948
+ }
949
+
950
+ .color-input-text {
951
+ box-sizing: border-box;
952
+ width: 100%;
953
+ padding: 0.375rem 0.5rem 0.375rem 2.25rem;
954
+ border: 1px solid
955
+ var(--color-input-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
956
+ border-radius: 0.375rem;
957
+ background: var(
958
+ --color-input-bg,
959
+ var(--surface, light-dark(#ffffff, #1d2024))
960
+ );
961
+ color: inherit;
962
+ font: inherit;
963
+ }
964
+
965
+ .color-input-text:disabled {
966
+ cursor: not-allowed;
967
+ opacity: 0.6;
968
+ }
969
+
970
+ .color-input-picker {
971
+ position: absolute;
972
+ left: 0.5rem;
973
+ width: 1.25rem;
974
+ height: 1.25rem;
975
+ padding: 0;
976
+ border: none;
977
+ background: none;
978
+ cursor: pointer;
979
+ appearance: none;
980
+ }
981
+
982
+ .color-input-picker:disabled {
983
+ cursor: not-allowed;
984
+ opacity: 0.6;
985
+ }
986
+
987
+ .color-input-picker::-webkit-color-swatch-wrapper {
988
+ padding: 0;
989
+ }
990
+
991
+ .color-input-picker::-webkit-color-swatch {
992
+ border: 1px solid
993
+ var(--color-input-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
994
+ border-radius: 0.25rem;
995
+ }
996
+
997
+ .color-input-picker::-moz-color-swatch {
998
+ border: 1px solid
999
+ var(--color-input-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
1000
+ border-radius: 0.25rem;
1001
+ }
1002
+
752
1003
  /* SearchBar: input with a leading icon and trailing clear button / shortcut hint. */
753
1004
  .search-bar {
754
1005
  position: relative;
@@ -833,3 +1084,89 @@
833
1084
  display: none;
834
1085
  }
835
1086
  }
1087
+
1088
+ /* Modal: native `<dialog>` in the top layer; the panel fills it so that only
1089
+ backdrop clicks target the dialog itself. */
1090
+ .modal {
1091
+ box-sizing: border-box;
1092
+ width: min(var(--modal-width, 32rem), calc(100vw - 2rem));
1093
+ max-height: calc(100dvh - 2rem);
1094
+ padding: 0;
1095
+ border: 1px solid
1096
+ var(--modal-border, var(--border, light-dark(#d0d0d0, #30343a)));
1097
+ border-radius: 0.5rem;
1098
+ background: var(--modal-bg, var(--surface, light-dark(#ffffff, #1d2024)));
1099
+ color: inherit;
1100
+ box-shadow: 0 16px 48px rgb(0 0 0 / 20%);
1101
+ }
1102
+
1103
+ .modal::backdrop {
1104
+ background: var(--modal-backdrop, rgb(0 0 0 / 45%));
1105
+ }
1106
+
1107
+ /* Keep the page behind still while a modal is open. */
1108
+ :root:has(.modal[open]) {
1109
+ overflow: hidden;
1110
+ }
1111
+
1112
+ .modal-panel {
1113
+ display: flex;
1114
+ flex-direction: column;
1115
+ max-height: inherit;
1116
+ }
1117
+
1118
+ .modal-header {
1119
+ display: flex;
1120
+ align-items: center;
1121
+ gap: 0.5rem;
1122
+ padding: 0.75rem 0.75rem 0 1rem;
1123
+ }
1124
+
1125
+ .modal-title {
1126
+ flex: 1;
1127
+ margin: 0;
1128
+ font-size: 1.125rem;
1129
+ font-weight: 600;
1130
+ }
1131
+
1132
+ .modal-close {
1133
+ display: grid;
1134
+ flex: none;
1135
+ place-items: center;
1136
+ width: 32px;
1137
+ height: 32px;
1138
+ margin-left: auto;
1139
+ padding: 0;
1140
+ border: 1px solid transparent;
1141
+ border-radius: 50%;
1142
+ background: transparent;
1143
+ color: var(
1144
+ --modal-muted,
1145
+ var(--text-secondary, light-dark(#616161, #9b9da5))
1146
+ );
1147
+ cursor: pointer;
1148
+ }
1149
+
1150
+ .modal-close:hover {
1151
+ background: var(
1152
+ --modal-hover-bg,
1153
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
1154
+ );
1155
+ color: inherit;
1156
+ }
1157
+
1158
+ .modal-body {
1159
+ flex: 1;
1160
+ min-height: 0;
1161
+ overflow-y: auto;
1162
+ padding: 0.75rem 1rem 1rem;
1163
+ }
1164
+
1165
+ .modal-footer {
1166
+ display: flex;
1167
+ justify-content: flex-end;
1168
+ gap: 0.5rem;
1169
+ padding: 0.75rem 1rem;
1170
+ border-top: 1px solid
1171
+ var(--modal-border, var(--border, light-dark(#d0d0d0, #30343a)));
1172
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serkonda7/solid-components",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "Reusable components for SolidJS",
5
5
  "license": "MIT",
6
6
  "author": "serkonda7",
@@ -31,27 +31,27 @@
31
31
  "scripts": {
32
32
  "lint": "biome check --write",
33
33
  "lint:ci": "biome ci",
34
- "check": "bun check",
34
+ "typecheck": "bun check",
35
35
  "test": "vitest run",
36
36
  "examples": "vite --config examples/vite.config.ts",
37
37
  "build:dev": "bun run scripts/build.ts --dev",
38
- "build": "bun run check && bun run scripts/build.ts --prod",
38
+ "build": "bun run typecheck && bun run scripts/build.ts --prod",
39
39
  "prepack": "bun run build"
40
40
  },
41
41
  "engines": {
42
42
  "node": ">=24"
43
43
  },
44
- "packageManager": "bun@1.4.3",
44
+ "packageManager": "bun@1.4.4",
45
45
  "devDependencies": {
46
- "@biomejs/biome": "2.5.15",
47
- "@tabler/icons-solidjs": "~3.49.0",
48
- "@types/bun": "1.4.3",
46
+ "@biomejs/biome": "2.6.4",
47
+ "@tabler/icons-solidjs": "~3.50.0",
48
+ "@types/bun": "1.4.4",
49
49
  "@vitest/browser-playwright": "~5.0.5",
50
- "playwright": "~1.64.0",
51
- "solid-js": "~1.9.17",
50
+ "playwright": "~1.64.1",
51
+ "solid-js": "~1.9.18",
52
52
  "typescript": "~7.0.2",
53
- "vite": "~8.3.4",
54
- "vite-plugin-solid": "~2.11.14",
53
+ "vite": "~8.3.5",
54
+ "vite-plugin-solid": "~2.11.15",
55
55
  "vitest": "~5.0.5"
56
56
  },
57
57
  "peerDependencies": {
@@ -62,5 +62,8 @@
62
62
  "vite": {
63
63
  "optional": true
64
64
  }
65
+ },
66
+ "dependencies": {
67
+ "@tanstack/solid-table": "~9.3.1"
65
68
  }
66
69
  }