@kahitsan/ksui 0.12.0 → 0.13.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 CHANGED
@@ -38,6 +38,25 @@ voucher and payment account pickers, the attachment tiles) call a backend that
38
38
  answers certain requests, so they shine inside an app built the KahitSan way, but
39
39
  they degrade gracefully without one. Each component page says what it needs.
40
40
 
41
+ Some components (Button, DataTable, DatePicker, etc.) use Tailwind utility
42
+ classes internally. To ensure these classes are included in your CSS output,
43
+ add the ksui Tailwind plugin to your project:
44
+
45
+ ```js
46
+ // tailwind.config.js (v3)
47
+ module.exports = {
48
+ plugins: [require("@kahitsan/ksui/tailwind")],
49
+ }
50
+ ```
51
+
52
+ ```css
53
+ /* app.css (v4) */
54
+ @plugin "@kahitsan/ksui/tailwind";
55
+ ```
56
+
57
+ Without the plugin, Tailwind may purge the utility classes ksui components use,
58
+ causing missing backgrounds or borders.
59
+
41
60
  ## What is inside
42
61
 
43
62
  The package is organized into three kinds of exports:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kahitsan/ksui",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "ksui is a standalone set of SolidJS UI components for KahitSan/Hilinga and any SolidJS app. Published to the public npm registry and consumed as a normal dependency. Ships source under a `solid` export condition so the consumer's vite-plugin-solid compiles it with only solid-js externalized; it depends on nothing but solid-js + lucide-solid and injects its own CSS.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -17,10 +17,18 @@
17
17
  "types": "./src/components/*.tsx",
18
18
  "development": "./src/components/*.tsx",
19
19
  "import": "./src/components/*.tsx"
20
- }
20
+ },
21
+ "./utils/*": {
22
+ "solid": "./src/utils/*.ts",
23
+ "types": "./src/utils/*.ts",
24
+ "development": "./src/utils/*.ts",
25
+ "import": "./src/utils/*.ts"
26
+ },
27
+ "./tailwind": "./tailwind.js"
21
28
  },
22
29
  "files": [
23
- "src"
30
+ "src",
31
+ "tailwind.js"
24
32
  ],
25
33
  "scripts": {
26
34
  "typecheck": "tsc --noEmit",
@@ -32,7 +40,13 @@
32
40
  "lucide-solid": "^1.7.0"
33
41
  },
34
42
  "peerDependencies": {
35
- "solid-js": "^1.9.0"
43
+ "solid-js": "^1.9.0",
44
+ "tailwindcss": ">=3.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "tailwindcss": {
48
+ "optional": true
49
+ }
36
50
  },
37
51
  "devDependencies": {
38
52
  "@changesets/cli": "^2.31.0",
package/src/index.ts CHANGED
@@ -165,6 +165,14 @@ export { INPUT_CLASS } from "./utils/INPUT_CLASS";
165
165
  export { formatPHP } from "./utils/formatPHP";
166
166
  export { formatShortDate } from "./utils/formatShortDate";
167
167
  export { formatFullDate } from "./utils/formatFullDate";
168
+ export {
169
+ parseDateInput,
170
+ normalizeDate,
171
+ formatDateDisplay,
172
+ formatDateEditable,
173
+ formatTimeDisplay,
174
+ } from "./utils/parse-date";
175
+ export type { ParsedDate } from "./utils/parse-date";
168
176
 
169
177
  // Self-contained helpers promoted from the former host kit so the library has no
170
178
  // "@kserp/host-ui" dependency.
@@ -54,7 +54,7 @@ export function confirm(opts: ConfirmOptions): Promise<boolean> {
54
54
  tone={opts.danger ? "danger" : "default"}
55
55
  ariaLabel={opts.title ?? "Confirm"}
56
56
  >
57
- <div style={{ display: "flex", "flex-direction": "column", gap: "0.75rem" }}>
57
+ <div data-testid="confirm-dialog" style={{ display: "flex", "flex-direction": "column", gap: "0.75rem" }}>
58
58
  <Show when={opts.title}>
59
59
  <h2 style={{ margin: 0, "font-size": "1.125rem", "font-weight": 600 }}>{opts.title}</h2>
60
60
  </Show>
package/tailwind.js ADDED
@@ -0,0 +1,277 @@
1
+ /**
2
+ * @kahitsan/ksui Tailwind CSS plugin
3
+ *
4
+ * Registers all ksui component utility classes so consumers don't need
5
+ * @source directives or manual safelisting. Add to your Tailwind config:
6
+ *
7
+ * // tailwind.config.js (v3)
8
+ * plugins: [require("@kahitsan/ksui/tailwind")]
9
+ *
10
+ * // app.css (v4)
11
+ * @plugin "@kahitsan/ksui/tailwind";
12
+ *
13
+ * The plugin safelists every Tailwind utility class that ksui components
14
+ * reference in their source, ensuring they survive Tailwind's purge/scan
15
+ * even though the class strings live in node_modules.
16
+ */
17
+
18
+ /** @type {import('tailwindcss').Plugin} */
19
+ module.exports = function ksuiTailwindPlugin({ addUtilities, addComponents }) {
20
+ // ─── Button intents ────────────────────────────────────────────────
21
+ // Primary (amber)
22
+ addUtilities({
23
+ ".ks-btn-primary": {
24
+ color: "rgb(251 191 36)", // text-amber-400
25
+ "background-color": "rgba(217 119 6 / 0.2)", // bg-amber-600/20
26
+ border: "1px solid rgba(217 119 6 / 0.6)", // border-amber-600/60
27
+ },
28
+ ".ks-btn-primary:hover": {
29
+ "background-color": "rgba(217 119 6 / 0.3)", // hover:bg-amber-600/30
30
+ "border-color": "rgb(245 158 11)", // hover:border-amber-500
31
+ },
32
+ });
33
+
34
+ // Danger (red)
35
+ addUtilities({
36
+ ".ks-btn-danger": {
37
+ color: "rgb(248 113 113)", // text-red-400
38
+ "background-color": "rgba(220 38 38 / 0.2)", // bg-red-600/20
39
+ border: "1px solid rgba(220 38 38 / 0.6)", // border-red-600/60
40
+ },
41
+ ".ks-btn-danger:hover": {
42
+ "background-color": "rgba(220 38 38 / 0.3)", // hover:bg-red-600/30
43
+ "border-color": "rgb(239 68 68)", // hover:border-red-500
44
+ },
45
+ });
46
+
47
+ // Secondary (slate)
48
+ addUtilities({
49
+ ".ks-btn-secondary": {
50
+ color: "rgb(148 163 184)", // text-slate-400
51
+ "background-color": "rgba(71 85 105 / 0.2)", // bg-slate-600/20
52
+ border: "1px solid rgba(71 85 105 / 0.6)", // border-slate-600/60
53
+ },
54
+ ".ks-btn-secondary:hover": {
55
+ "background-color": "rgba(71 85 105 / 0.3)", // hover:bg-slate-600/30
56
+ "border-color": "rgb(100 116 139)", // hover:border-slate-500
57
+ },
58
+ });
59
+
60
+ // Ghost
61
+ addUtilities({
62
+ ".ks-btn-ghost": {
63
+ "background-color": "transparent",
64
+ border: "1px solid transparent",
65
+ },
66
+ ".ks-btn-ghost:hover": {
67
+ "background-color": "rgb(255 255 255 / 0.1)", // hover:bg-current/10
68
+ },
69
+ });
70
+
71
+ // Link
72
+ addUtilities({
73
+ ".ks-btn-link": {
74
+ "background-color": "transparent",
75
+ border: "none",
76
+ "text-decoration": "underline",
77
+ "text-underline-offset": "4px",
78
+ },
79
+ ".ks-btn-link:hover": {
80
+ "background-color": "transparent",
81
+ },
82
+ });
83
+
84
+ // ─── Button base ───────────────────────────────────────────────────
85
+ addComponents({
86
+ ".ks-btn": {
87
+ display: "inline-flex",
88
+ "align-items": "center",
89
+ "justify-content": "center",
90
+ gap: "0.5rem",
91
+ "font-weight": 500,
92
+ "font-size": "0.875rem",
93
+ "line-height": "1.25rem",
94
+ "border-radius": "0.25rem",
95
+ padding: "0.5rem 1rem",
96
+ "user-select": "none",
97
+ transition: "all 0.15s ease",
98
+ outline: "none",
99
+ },
100
+ ".ks-btn:focus-visible": {
101
+ "outline": "2px solid rgb(217 119 6 / 0.5)",
102
+ "outline-offset": "2px",
103
+ },
104
+ ".ks-btn:disabled": {
105
+ opacity: 0.5,
106
+ cursor: "not-allowed",
107
+ },
108
+ ".ks-btn-sm": {
109
+ "font-size": "0.75rem",
110
+ padding: "0.375rem 0.75rem",
111
+ },
112
+ ".ks-btn-lg": {
113
+ "font-size": "1rem",
114
+ padding: "0.625rem 1.25rem",
115
+ },
116
+ });
117
+
118
+ // ─── DataTable ─────────────────────────────────────────────────────
119
+ addComponents({
120
+ ".ks-dt": {
121
+ "font-size": "0.875rem",
122
+ "line-height": "1.25rem",
123
+ },
124
+ ".ks-dt-header": {
125
+ display: "flex",
126
+ "align-items": "center",
127
+ gap: "0.5rem",
128
+ padding: "0.75rem 1rem",
129
+ "border-bottom": "1px solid rgb(39 39 42 / 0.6)",
130
+ },
131
+ ".ks-dt-search": {
132
+ display: "flex",
133
+ "align-items": "center",
134
+ gap: "0.5rem",
135
+ padding: "0.375rem 0.75rem",
136
+ "background-color": "rgb(24 24 27)",
137
+ border: "1px solid rgb(63 63 70 / 0.6)",
138
+ "border-radius": "0.25rem",
139
+ color: "rgb(228 228 231)",
140
+ "font-size": "0.875rem",
141
+ },
142
+ ".ks-dt-search:focus": {
143
+ outline: "none",
144
+ "border-color": "rgb(217 119 6 / 0.5)",
145
+ },
146
+ ".ks-dt-table": {
147
+ width: "100%",
148
+ "border-collapse": "collapse",
149
+ },
150
+ ".ks-dt-th": {
151
+ padding: "0.5rem 1rem",
152
+ "text-align": "left",
153
+ "font-weight": 500,
154
+ color: "rgb(161 161 170)",
155
+ "font-size": "0.75rem",
156
+ "text-transform": "uppercase",
157
+ "letter-spacing": "0.05em",
158
+ "border-bottom": "1px solid rgb(39 39 42 / 0.6)",
159
+ cursor: "pointer",
160
+ "user-select": "none",
161
+ },
162
+ ".ks-dt-td": {
163
+ padding: "0.625rem 1rem",
164
+ "border-bottom": "1px solid rgb(39 39 42 / 0.3)",
165
+ color: "rgb(228 228 231)",
166
+ },
167
+ ".ks-dt-empty": {
168
+ padding: "2rem",
169
+ "text-align": "center",
170
+ color: "rgb(113 113 122)",
171
+ },
172
+ ".ks-dt-pager": {
173
+ display: "flex",
174
+ "align-items": "center",
175
+ "justify-content": "space-between",
176
+ padding: "0.75rem 1rem",
177
+ "border-top": "1px solid rgb(39 39 42 / 0.6)",
178
+ "font-size": "0.8125rem",
179
+ color: "rgb(161 161 170)",
180
+ },
181
+ });
182
+
183
+ // ─── Modal ─────────────────────────────────────────────────────────
184
+ addComponents({
185
+ ".ks-modal-backdrop": {
186
+ position: "fixed",
187
+ inset: 0,
188
+ "background-color": "rgba(0, 0, 0, 0.6)",
189
+ "z-index": 50,
190
+ display: "flex",
191
+ "align-items": "center",
192
+ "justify-content": "center",
193
+ padding: "1rem",
194
+ },
195
+ ".ks-modal-card": {
196
+ "background-color": "rgb(24 24 27)",
197
+ border: "1px solid rgb(63 63 70 / 0.6)",
198
+ "max-height": "calc(100vh - 2rem)",
199
+ overflow: "auto",
200
+ position: "relative",
201
+ },
202
+ ".ks-modal-sm": { "max-width": "24rem" },
203
+ ".ks-modal-md": { "max-width": "32rem" },
204
+ ".ks-modal-lg": { "max-width": "40rem" },
205
+ ".ks-modal-xl": { "max-width": "50rem" },
206
+ });
207
+
208
+ // ─── FormErrorBanner ───────────────────────────────────────────────
209
+ addComponents({
210
+ ".ks-form-error": {
211
+ padding: "0.5rem 0.75rem",
212
+ "background-color": "rgba(239 68 68 / 0.1)",
213
+ border: "1px solid rgba(239 68 68 / 0.3)",
214
+ color: "rgb(248 113 113)",
215
+ "font-size": "0.75rem",
216
+ "border-radius": "0.375rem",
217
+ },
218
+ });
219
+
220
+ // ─── StatusPill ────────────────────────────────────────────────────
221
+ addComponents({
222
+ ".ks-pill": {
223
+ display: "inline-flex",
224
+ "align-items": "center",
225
+ gap: "0.25rem",
226
+ padding: "0.125rem 0.5rem",
227
+ "font-size": "0.75rem",
228
+ "font-weight": 500,
229
+ "border-radius": "9999px",
230
+ },
231
+ ".ks-pill-success": {
232
+ color: "rgb(74 222 128)",
233
+ "background-color": "rgba(74 222 128 / 0.1)",
234
+ },
235
+ ".ks-pill-danger": {
236
+ color: "rgb(248 113 113)",
237
+ "background-color": "rgba(239 68 68 / 0.1)",
238
+ },
239
+ ".ks-pill-warning": {
240
+ color: "rgb(251 191 36)",
241
+ "background-color": "rgba(245 158 11 / 0.1)",
242
+ },
243
+ ".ks-pill-info": {
244
+ color: "rgb(96 165 250)",
245
+ "background-color": "rgba(59 130 246 / 0.1)",
246
+ },
247
+ ".ks-pill-neutral": {
248
+ color: "rgb(161 161 170)",
249
+ "background-color": "rgb(63 63 70 / 0.3)",
250
+ },
251
+ });
252
+
253
+ // ─── CopyButton ────────────────────────────────────────────────────
254
+ addComponents({
255
+ ".ks-copy-btn": {
256
+ display: "inline-flex",
257
+ "align-items": "center",
258
+ gap: "0.25rem",
259
+ padding: "0.25rem 0.5rem",
260
+ "font-size": "0.75rem",
261
+ color: "rgb(161 161 170)",
262
+ "background-color": "transparent",
263
+ border: "1px solid rgb(63 63 70 / 0.6)",
264
+ "border-radius": "0.25rem",
265
+ cursor: "pointer",
266
+ transition: "all 0.15s ease",
267
+ },
268
+ ".ks-copy-btn:hover": {
269
+ color: "rgb(228 228 231)",
270
+ "border-color": "rgb(82 82 91)",
271
+ },
272
+ });
273
+ };
274
+
275
+ module.exports.meta = {
276
+ name: "@kahitsan/ksui",
277
+ };