@groundbrick/svelte-ui 0.1.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.
Files changed (60) hide show
  1. package/README.md +125 -0
  2. package/dist/components/Alert.svelte +335 -0
  3. package/dist/components/Alert.svelte.d.ts +24 -0
  4. package/dist/components/AutocompleteInput.svelte +356 -0
  5. package/dist/components/AutocompleteInput.svelte.d.ts +72 -0
  6. package/dist/components/Badge.svelte +185 -0
  7. package/dist/components/Badge.svelte.d.ts +20 -0
  8. package/dist/components/Button.svelte +415 -0
  9. package/dist/components/Button.svelte.d.ts +34 -0
  10. package/dist/components/Card.svelte +181 -0
  11. package/dist/components/Card.svelte.d.ts +24 -0
  12. package/dist/components/CardBody.svelte +78 -0
  13. package/dist/components/CardBody.svelte.d.ts +12 -0
  14. package/dist/components/CardFooter.svelte +81 -0
  15. package/dist/components/CardFooter.svelte.d.ts +14 -0
  16. package/dist/components/CardHeader.svelte +186 -0
  17. package/dist/components/CardHeader.svelte.d.ts +21 -0
  18. package/dist/components/Col.svelte +172 -0
  19. package/dist/components/Col.svelte.d.ts +26 -0
  20. package/dist/components/Container.svelte +118 -0
  21. package/dist/components/Container.svelte.d.ts +14 -0
  22. package/dist/components/Drawer.svelte +233 -0
  23. package/dist/components/Drawer.svelte.d.ts +13 -0
  24. package/dist/components/Dropdown.svelte +190 -0
  25. package/dist/components/Dropdown.svelte.d.ts +26 -0
  26. package/dist/components/DropdownItem.svelte +103 -0
  27. package/dist/components/DropdownItem.svelte.d.ts +22 -0
  28. package/dist/components/DurationInput.svelte +170 -0
  29. package/dist/components/DurationInput.svelte.d.ts +27 -0
  30. package/dist/components/EditableTable.svelte +647 -0
  31. package/dist/components/EditableTable.svelte.d.ts +74 -0
  32. package/dist/components/EmptyState.svelte +192 -0
  33. package/dist/components/EmptyState.svelte.d.ts +22 -0
  34. package/dist/components/FormField.svelte +260 -0
  35. package/dist/components/FormField.svelte.d.ts +68 -0
  36. package/dist/components/GridView.svelte +1022 -0
  37. package/dist/components/GridView.svelte.d.ts +38 -0
  38. package/dist/components/GridView.types.d.ts +28 -0
  39. package/dist/components/GridView.types.js +1 -0
  40. package/dist/components/LoadingSpinner.svelte +253 -0
  41. package/dist/components/LoadingSpinner.svelte.d.ts +17 -0
  42. package/dist/components/Modal.svelte +473 -0
  43. package/dist/components/Modal.svelte.d.ts +42 -0
  44. package/dist/components/PhoneInput.svelte +406 -0
  45. package/dist/components/PhoneInput.svelte.d.ts +31 -0
  46. package/dist/components/PhotoUpload.svelte +529 -0
  47. package/dist/components/PhotoUpload.svelte.d.ts +46 -0
  48. package/dist/components/Row.svelte +153 -0
  49. package/dist/components/Row.svelte.d.ts +18 -0
  50. package/dist/icons/PawPrintIcon.svelte +41 -0
  51. package/dist/icons/PawPrintIcon.svelte.d.ts +14 -0
  52. package/dist/index.d.ts +41 -0
  53. package/dist/index.js +49 -0
  54. package/dist/styles/forms.css +182 -0
  55. package/dist/styles/tokens.css +243 -0
  56. package/dist/utils/duration.d.ts +20 -0
  57. package/dist/utils/duration.js +40 -0
  58. package/dist/utils/scrollLock.d.ts +7 -0
  59. package/dist/utils/scrollLock.js +26 -0
  60. package/package.json +66 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Design Tokens - @groundbrick/svelte-ui
3
+ * Default theme tokens for the component library.
4
+ *
5
+ * Consumers can import this file for sane defaults and override any CSS
6
+ * custom property (globally or on a wrapper element) to re-theme components.
7
+ * Components reference these variables exclusively, so overriding a token
8
+ * re-skins every component that uses it.
9
+ */
10
+
11
+ :root {
12
+ /* ========== BRAND COLORS ========== */
13
+ --color-cyan: #15B9FF;
14
+ --color-violet: #7B3FF2;
15
+ --color-magenta: #E414FF;
16
+
17
+ /* ========== PRIMARY COLORS ========== */
18
+ --color-primary: #7B3FF2;
19
+ --color-primary-hover: #6B33E6;
20
+ --color-primary-active: #5A27D6;
21
+ --color-primary-light: rgba(123, 63, 242, 0.1);
22
+ --color-primary-dark: #4A1FBF;
23
+
24
+ /* ========== GRADIENTS ========== */
25
+ --gradient-brand: linear-gradient(35deg, #15B9FF 0%, #7B3FF2 52%, #E414FF 100%);
26
+ --gradient-brand-hover: linear-gradient(35deg, #7B3FF2 0%, #C51BFF 45%, #FF2BD6 100%);
27
+ --gradient-brand-vertical: linear-gradient(180deg, #15B9FF 0%, #7B3FF2 50%, #E414FF 100%);
28
+ --gradient-card-header: linear-gradient(135deg, rgba(21, 185, 255, 0.08) 0%, rgba(123, 63, 242, 0.08) 50%, rgba(228, 20, 255, 0.06) 100%);
29
+
30
+ /* ========== SEMANTIC COLORS ========== */
31
+ --color-secondary: #5B5F73;
32
+ --color-secondary-hover: #4D5163;
33
+ --color-secondary-light: rgba(91, 95, 115, 0.1);
34
+
35
+ --color-success: #18B77E;
36
+ --color-success-hover: #128A5E;
37
+ --color-success-light: rgba(24, 183, 126, 0.1);
38
+
39
+ --color-danger: #E4475A;
40
+ --color-danger-hover: #C33545;
41
+ --color-danger-light: rgba(228, 71, 90, 0.1);
42
+
43
+ --color-warning: #F2B705;
44
+ --color-warning-hover: #D99E00;
45
+ --color-warning-light: rgba(242, 183, 5, 0.1);
46
+
47
+ --color-info: #18A7D6;
48
+ --color-info-hover: #1287AD;
49
+ --color-info-light: rgba(24, 167, 214, 0.1);
50
+
51
+ /* ========== SEMANTIC BACKGROUNDS ========== */
52
+ --color-success-bg: #E9FBF4;
53
+ --color-success-border: #BFF0DC;
54
+ --color-success-text: #0B6B4A;
55
+
56
+ --color-info-bg: #E7F7FD;
57
+ --color-info-border: #BDEAF7;
58
+ --color-info-text: #0B5E7A;
59
+
60
+ --color-warning-bg: #FFF6DD;
61
+ --color-warning-border: #FFE5A3;
62
+ --color-warning-text: #ea961f;
63
+ --color-warning-text-dark: #8e723b;
64
+
65
+ --color-danger-bg: #FEECEF;
66
+ --color-danger-border: #F7C6CD;
67
+ --color-danger-text: #8A1E2B;
68
+
69
+ /* ========== NEUTRAL COLORS ========== */
70
+ --color-bg-app: #F6F7FA;
71
+ --color-bg-surface: #FFFFFF;
72
+ --color-bg-surface-2: #F1F3F8;
73
+ --color-border-subtle: #E6E8F0;
74
+ --color-border-strong: #D5D9E6;
75
+ --color-text: #2F2F33;
76
+ --color-text-muted: #4D5163;
77
+ --color-text-subtle: #5B5F73;
78
+
79
+ /* Grayscale (kept for compatibility) */
80
+ --color-white: #FFFFFF;
81
+ --color-gray-50: #F6F7FA;
82
+ --color-gray-100: #F1F3F8;
83
+ --color-gray-200: #E6E8F0;
84
+ --color-gray-300: #D5D9E6;
85
+ --color-gray-400: #6C6F7A;
86
+ --color-gray-500: #5B5F73;
87
+ --color-gray-600: #4D5163;
88
+ --color-gray-700: #3D3F4A;
89
+ --color-gray-800: #2F2F33;
90
+ --color-gray-900: #1E1F25;
91
+ --color-black: #000000;
92
+
93
+ /* ========== STATUS COLORS ========== */
94
+ --status-pending: var(--color-warning);
95
+ --status-confirmed: var(--color-info);
96
+ --status-completed: var(--color-success);
97
+ --status-cancelled: var(--color-danger);
98
+ --status-no-show: var(--color-secondary);
99
+
100
+ /* ========== SPACING ========== */
101
+ --spacing-0: 0;
102
+ --spacing-xs: 0.25rem; /* 4px */
103
+ --spacing-sm: 0.5rem; /* 8px */
104
+ --spacing-md: 1rem; /* 16px */
105
+ --spacing-lg: 1.5rem; /* 24px */
106
+ --spacing-xl: 2rem; /* 32px */
107
+ --spacing-2xl: 3rem; /* 48px */
108
+ --spacing-3xl: 4rem; /* 64px */
109
+
110
+ /* ========== TYPOGRAPHY ========== */
111
+ --font-family-base: 'Baloo 2', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
112
+ --font-family-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
113
+
114
+ /* Font sizes */
115
+ --font-size-xs: 0.8125rem; /* 13px */
116
+ --font-size-sm: 0.9375rem; /* 15px */
117
+ --font-size-base: 1rem; /* 16px */
118
+ --font-size-lg: 1.125rem; /* 18px */
119
+ --font-size-xl: 1.25rem; /* 20px */
120
+ --font-size-2xl: 1.5rem; /* 24px */
121
+ --font-size-3xl: 2rem; /* 32px */
122
+ --font-size-4xl: 2.5rem; /* 40px */
123
+
124
+ /* Font weights */
125
+ --font-weight-light: 400;
126
+ --font-weight-normal: 400;
127
+ --font-weight-medium: 500;
128
+ --font-weight-semibold: 500;
129
+ --font-weight-bold: 600;
130
+ --font-weight-extrabold: 600;
131
+
132
+ /* Line heights */
133
+ --line-height-tight: 1.25;
134
+ --line-height-normal: 1.5;
135
+ --line-height-relaxed: 1.75;
136
+ --line-height-loose: 2;
137
+
138
+ /* ========== BORDER RADIUS ========== */
139
+ --radius-none: 0;
140
+ --radius-sm: 0.5rem; /* 8px */
141
+ --radius-md: 0.75rem; /* 12px */
142
+ --radius-lg: 1rem; /* 16px */
143
+ --radius-xl: 1.5rem; /* 24px */
144
+ --radius-2xl: 2rem; /* 32px */
145
+ --radius-full: 9999px; /* Pill shape */
146
+
147
+ /* ========== SHADOWS ========== */
148
+ --shadow-none: none;
149
+ --shadow-xs: 0 1px 2px 0 rgba(18, 24, 40, 0.04);
150
+ --shadow-sm: 0 2px 4px 0 rgba(18, 24, 40, 0.06);
151
+ --shadow-md: 0 4px 12px rgba(18, 24, 40, 0.08);
152
+ --shadow-lg: 0 8px 26px rgba(18, 24, 40, 0.08);
153
+ --shadow-xl: 0 14px 34px rgba(18, 24, 40, 0.12);
154
+ --shadow-2xl: 0 25px 50px -12px rgba(18, 24, 40, 0.2);
155
+
156
+ /* Colored shadows (for buttons and hover) */
157
+ --shadow-primary: 0 10px 24px rgba(123, 63, 242, 0.16);
158
+ --shadow-primary-hover: 0 12px 28px rgba(123, 63, 242, 0.20);
159
+ --shadow-gradient: 0 10px 24px rgba(228, 20, 255, 0.16);
160
+ --shadow-success: 0 8px 20px rgba(24, 183, 126, 0.16);
161
+ --shadow-danger: 0 8px 20px rgba(228, 71, 90, 0.16);
162
+
163
+ /* Inner shadows */
164
+ --shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.05);
165
+
166
+ /* ========== FOCUS RING ========== */
167
+ --focus-ring-color: rgba(123, 63, 242, 0.16);
168
+ --focus-ring-width: 4px;
169
+
170
+ /* ========== TRANSITIONS ========== */
171
+ --transition-fast: 150ms ease;
172
+ --transition-base: 180ms ease;
173
+ --transition-slow: 300ms ease;
174
+ --transition-slower: 500ms ease;
175
+
176
+ /* Transition curves */
177
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
178
+ --ease-out: cubic-bezier(0, 0, 0.2, 1);
179
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
180
+
181
+ /* ========== Z-INDEX ========== */
182
+ --z-dropdown: 1000;
183
+ --z-sticky: 1020;
184
+ --z-fixed: 1030;
185
+ --z-modal-backdrop: 1040;
186
+ --z-modal: 1050;
187
+ --z-popover: 1060;
188
+ --z-tooltip: 1070;
189
+ --z-toast: 1080;
190
+ --z-top: 9999;
191
+
192
+ /* ========== BREAKPOINTS ========== */
193
+ --breakpoint-xs: 0;
194
+ --breakpoint-sm: 576px;
195
+ --breakpoint-md: 768px;
196
+ --breakpoint-lg: 992px;
197
+ --breakpoint-xl: 1200px;
198
+ --breakpoint-2xl: 1400px;
199
+
200
+ /* ========== CONTAINER WIDTHS ========== */
201
+ --container-sm: 540px;
202
+ --container-md: 720px;
203
+ --container-lg: 960px;
204
+ --container-xl: 1140px;
205
+ --container-2xl: 1320px;
206
+
207
+ /* ========== OPACITIES ========== */
208
+ --opacity-0: 0;
209
+ --opacity-5: 0.05;
210
+ --opacity-10: 0.1;
211
+ --opacity-20: 0.2;
212
+ --opacity-25: 0.25;
213
+ --opacity-30: 0.3;
214
+ --opacity-40: 0.4;
215
+ --opacity-50: 0.5;
216
+ --opacity-60: 0.6;
217
+ --opacity-70: 0.7;
218
+ --opacity-75: 0.75;
219
+ --opacity-80: 0.8;
220
+ --opacity-90: 0.9;
221
+ --opacity-95: 0.95;
222
+ --opacity-100: 1;
223
+
224
+ /* ========== BORDERS ========== */
225
+ --border-width-thin: 1px;
226
+ --border-width-medium: 2px;
227
+ --border-width-thick: 4px;
228
+
229
+ --border-color-light: var(--color-border-subtle);
230
+ --border-color-medium: var(--color-border-strong);
231
+ --border-color-dark: var(--color-gray-400);
232
+
233
+ /* ========== COMPATIBILITY ALIASES ==========
234
+ Some components reference these shorthand names. They are defined here as
235
+ aliases so the library is self-contained. Override the canonical token and
236
+ the alias follows automatically. */
237
+ --color-surface: var(--color-bg-surface);
238
+ --color-border: var(--color-border-subtle);
239
+ --color-primary-soft: var(--color-primary-light);
240
+ --ap-text-muted: var(--color-text-muted);
241
+ --shadow-soft: var(--shadow-sm);
242
+ --shadow-hover: var(--shadow-lg);
243
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Duration helpers for hours + minutes inputs (used by DurationInput).
3
+ */
4
+ /**
5
+ * Format a total in minutes as a human-readable duration (e.g. "45 min", "1h", "1h 20min").
6
+ */
7
+ export declare function formatDuration(minutes: number): string;
8
+ /**
9
+ * Split a total duration in minutes into { hours, minutes } for hours+minutes inputs.
10
+ * Returns nulls when the total is null/undefined/invalid (i.e. an empty input).
11
+ */
12
+ export declare function minutesToHoursAndMinutes(total: number | null | undefined): {
13
+ hours: number | null;
14
+ minutes: number | null;
15
+ };
16
+ /**
17
+ * Combine hours + minutes into a total duration in minutes.
18
+ * Returns null when both parts are empty, so optional/override fields stay "unset".
19
+ */
20
+ export declare function hoursAndMinutesToMinutes(hours: number | null | undefined, minutes: number | null | undefined): number | null;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Duration helpers for hours + minutes inputs (used by DurationInput).
3
+ */
4
+ /**
5
+ * Format a total in minutes as a human-readable duration (e.g. "45 min", "1h", "1h 20min").
6
+ */
7
+ export function formatDuration(minutes) {
8
+ const mins = Math.round(Number(minutes));
9
+ if (mins < 60)
10
+ return `${mins} min`;
11
+ const hours = Math.floor(mins / 60);
12
+ const remainingMinutes = mins % 60;
13
+ return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}min` : `${hours}h`;
14
+ }
15
+ /**
16
+ * Split a total duration in minutes into { hours, minutes } for hours+minutes inputs.
17
+ * Returns nulls when the total is null/undefined/invalid (i.e. an empty input).
18
+ */
19
+ export function minutesToHoursAndMinutes(total) {
20
+ if (total === null || total === undefined || Number.isNaN(Number(total))) {
21
+ return { hours: null, minutes: null };
22
+ }
23
+ const mins = Math.max(0, Math.round(Number(total)));
24
+ return { hours: Math.floor(mins / 60), minutes: mins % 60 };
25
+ }
26
+ /**
27
+ * Combine hours + minutes into a total duration in minutes.
28
+ * Returns null when both parts are empty, so optional/override fields stay "unset".
29
+ */
30
+ export function hoursAndMinutesToMinutes(hours, minutes) {
31
+ const h = hours === null || hours === undefined || Number.isNaN(Number(hours))
32
+ ? null
33
+ : Math.max(0, Math.floor(Number(hours)));
34
+ const m = minutes === null || minutes === undefined || Number.isNaN(Number(minutes))
35
+ ? null
36
+ : Math.max(0, Math.floor(Number(minutes)));
37
+ if (h === null && m === null)
38
+ return null;
39
+ return (h ?? 0) * 60 + (m ?? 0);
40
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Body scroll lock helper for overlays (Modal, Drawer).
3
+ * Reference-counted so nested overlays restore the original overflow correctly.
4
+ * Framework-agnostic: guards on `document` instead of SvelteKit's `browser`.
5
+ */
6
+ export declare function lockBodyScroll(): void;
7
+ export declare function unlockBodyScroll(): void;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Body scroll lock helper for overlays (Modal, Drawer).
3
+ * Reference-counted so nested overlays restore the original overflow correctly.
4
+ * Framework-agnostic: guards on `document` instead of SvelteKit's `browser`.
5
+ */
6
+ let lockCount = 0;
7
+ let previousOverflow = null;
8
+ const isBrowser = typeof document !== "undefined";
9
+ export function lockBodyScroll() {
10
+ if (!isBrowser)
11
+ return;
12
+ if (lockCount === 0) {
13
+ previousOverflow = document.body.style.overflow || "";
14
+ document.body.style.overflow = "hidden";
15
+ }
16
+ lockCount += 1;
17
+ }
18
+ export function unlockBodyScroll() {
19
+ if (!isBrowser || lockCount === 0)
20
+ return;
21
+ lockCount = Math.max(0, lockCount - 1);
22
+ if (lockCount === 0) {
23
+ document.body.style.overflow = previousOverflow ?? "";
24
+ previousOverflow = null;
25
+ }
26
+ }
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@groundbrick/svelte-ui",
3
+ "version": "0.1.1",
4
+ "description": "Reusable Svelte 5 UI components (design system): buttons, cards, alerts, badges, form fields, dropdowns, modals, empty states and more. Themeable via CSS variables.",
5
+ "type": "module",
6
+ "svelte": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "svelte": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./styles/tokens.css": "./dist/styles/tokens.css",
14
+ "./styles/forms.css": "./dist/styles/forms.css"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "svelte-package -i src -o dist",
21
+ "dev": "svelte-package -w -i src -o dist",
22
+ "check": "svelte-check --tsconfig ./tsconfig.json",
23
+ "clean": "rm -rf dist .svelte-kit *.tsbuildinfo"
24
+ },
25
+ "keywords": [
26
+ "svelte",
27
+ "svelte5",
28
+ "ui",
29
+ "components",
30
+ "design-system",
31
+ "bootstrap"
32
+ ],
33
+ "author": "Cleiton Marques <cleiton.marques@200.systems>",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/200Systems/bitbrick-microframework.git",
38
+ "directory": "packages/svelte-ui"
39
+ },
40
+ "peerDependencies": {
41
+ "svelte": "^5.25.0",
42
+ "@200systems/shared": "workspace:*"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "bootstrap": {
46
+ "optional": true
47
+ },
48
+ "bootstrap-icons": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "@200systems/shared": "workspace:*",
54
+ "@sveltejs/package": "^2.3.0",
55
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
56
+ "svelte": "^5.25.0",
57
+ "svelte-check": "^4.0.0",
58
+ "typescript": "^5.8.3"
59
+ },
60
+ "engines": {
61
+ "node": ">=18.0.0"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }