@serkonda7/solid-components 0.3.2 → 0.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.
@@ -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;
@@ -642,6 +813,16 @@
642
813
  min-width: 0;
643
814
  }
644
815
 
816
+ .filter-table-range {
817
+ display: flex;
818
+ gap: 0.5rem;
819
+ }
820
+
821
+ .filter-table-range .filter-table-input {
822
+ flex: 1;
823
+ min-width: 0;
824
+ }
825
+
645
826
  .filter-table-form {
646
827
  /* Label column width and gap; group headings are indented by both so they
647
828
  line up with the inputs. */
@@ -749,6 +930,67 @@
749
930
  }
750
931
  }
751
932
 
933
+ /* ColorInput: hex input with a native color picker as its leading swatch. */
934
+ .color-input {
935
+ position: relative;
936
+ display: flex;
937
+ align-items: center;
938
+ width: 9rem;
939
+ }
940
+
941
+ .color-input-text {
942
+ box-sizing: border-box;
943
+ width: 100%;
944
+ padding: 0.375rem 0.5rem 0.375rem 2.25rem;
945
+ border: 1px solid
946
+ var(--color-input-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
947
+ border-radius: 0.375rem;
948
+ background: var(
949
+ --color-input-bg,
950
+ var(--surface, light-dark(#ffffff, #1d2024))
951
+ );
952
+ color: inherit;
953
+ font: inherit;
954
+ }
955
+
956
+ .color-input-text:disabled {
957
+ cursor: not-allowed;
958
+ opacity: 0.6;
959
+ }
960
+
961
+ .color-input-picker {
962
+ position: absolute;
963
+ left: 0.5rem;
964
+ width: 1.25rem;
965
+ height: 1.25rem;
966
+ padding: 0;
967
+ border: none;
968
+ background: none;
969
+ cursor: pointer;
970
+ appearance: none;
971
+ }
972
+
973
+ .color-input-picker:disabled {
974
+ cursor: not-allowed;
975
+ opacity: 0.6;
976
+ }
977
+
978
+ .color-input-picker::-webkit-color-swatch-wrapper {
979
+ padding: 0;
980
+ }
981
+
982
+ .color-input-picker::-webkit-color-swatch {
983
+ border: 1px solid
984
+ var(--color-input-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
985
+ border-radius: 0.25rem;
986
+ }
987
+
988
+ .color-input-picker::-moz-color-swatch {
989
+ border: 1px solid
990
+ var(--color-input-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
991
+ border-radius: 0.25rem;
992
+ }
993
+
752
994
  /* SearchBar: input with a leading icon and trailing clear button / shortcut hint. */
753
995
  .search-bar {
754
996
  position: relative;
@@ -833,3 +1075,89 @@
833
1075
  display: none;
834
1076
  }
835
1077
  }
1078
+
1079
+ /* Modal: native `<dialog>` in the top layer; the panel fills it so that only
1080
+ backdrop clicks target the dialog itself. */
1081
+ .modal {
1082
+ box-sizing: border-box;
1083
+ width: min(var(--modal-width, 32rem), calc(100vw - 2rem));
1084
+ max-height: calc(100dvh - 2rem);
1085
+ padding: 0;
1086
+ border: 1px solid
1087
+ var(--modal-border, var(--border, light-dark(#d0d0d0, #30343a)));
1088
+ border-radius: 0.5rem;
1089
+ background: var(--modal-bg, var(--surface, light-dark(#ffffff, #1d2024)));
1090
+ color: inherit;
1091
+ box-shadow: 0 16px 48px rgb(0 0 0 / 20%);
1092
+ }
1093
+
1094
+ .modal::backdrop {
1095
+ background: var(--modal-backdrop, rgb(0 0 0 / 45%));
1096
+ }
1097
+
1098
+ /* Keep the page behind still while a modal is open. */
1099
+ :root:has(.modal[open]) {
1100
+ overflow: hidden;
1101
+ }
1102
+
1103
+ .modal-panel {
1104
+ display: flex;
1105
+ flex-direction: column;
1106
+ max-height: inherit;
1107
+ }
1108
+
1109
+ .modal-header {
1110
+ display: flex;
1111
+ align-items: center;
1112
+ gap: 0.5rem;
1113
+ padding: 0.75rem 0.75rem 0 1rem;
1114
+ }
1115
+
1116
+ .modal-title {
1117
+ flex: 1;
1118
+ margin: 0;
1119
+ font-size: 1.125rem;
1120
+ font-weight: 600;
1121
+ }
1122
+
1123
+ .modal-close {
1124
+ display: grid;
1125
+ flex: none;
1126
+ place-items: center;
1127
+ width: 32px;
1128
+ height: 32px;
1129
+ margin-left: auto;
1130
+ padding: 0;
1131
+ border: 1px solid transparent;
1132
+ border-radius: 50%;
1133
+ background: transparent;
1134
+ color: var(
1135
+ --modal-muted,
1136
+ var(--text-secondary, light-dark(#616161, #9b9da5))
1137
+ );
1138
+ cursor: pointer;
1139
+ }
1140
+
1141
+ .modal-close:hover {
1142
+ background: var(
1143
+ --modal-hover-bg,
1144
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
1145
+ );
1146
+ color: inherit;
1147
+ }
1148
+
1149
+ .modal-body {
1150
+ flex: 1;
1151
+ min-height: 0;
1152
+ overflow-y: auto;
1153
+ padding: 0.75rem 1rem 1rem;
1154
+ }
1155
+
1156
+ .modal-footer {
1157
+ display: flex;
1158
+ justify-content: flex-end;
1159
+ gap: 0.5rem;
1160
+ padding: 0.75rem 1rem;
1161
+ border-top: 1px solid
1162
+ var(--modal-border, var(--border, light-dark(#d0d0d0, #30343a)));
1163
+ }
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.0",
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
  }