@keepui/ui 0.1.7 → 0.3.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
@@ -7,6 +7,29 @@
7
7
 
8
8
  ---
9
9
 
10
+ ## Table of Contents
11
+
12
+ - [Purpose](#purpose)
13
+ - [Package Structure](#package-structure)
14
+ - [Installation](#installation)
15
+ - [Setup](#setup)
16
+ - [1. Import styles](#1-import-styles)
17
+ - [2. Register providers](#2-register-providers)
18
+ - [Available Components](#available-components)
19
+ - [`<keepui-button>`](#keepui-button)
20
+ - [`<keepui-card>`](#keepui-card)
21
+ - [`<keepui-icon>`](#keepui-icon)
22
+ - [`<keepui-icon-action-button>`](#keepui-icon-action-button)
23
+ - [`<keepui-image-preview>`](#keepui-image-preview)
24
+ - [i18n](#i18n)
25
+ - [Theming](#theming)
26
+ - [Architecture Notes](#architecture-notes)
27
+ - [Testing](#testing)
28
+ - [Building](#building)
29
+ - [Publishing to npm](#publishing-to-npm)
30
+
31
+ ---
32
+
10
33
  ## Purpose
11
34
 
12
35
  KeepUI is a reusable Angular component library designed to work seamlessly across:
@@ -14,7 +37,7 @@ KeepUI is a reusable Angular component library designed to work seamlessly acros
14
37
  - **Standard Angular web applications** (browser only)
15
38
  - **Angular + Capacitor applications** (iOS, Android, PWA)
16
39
 
17
- The library uses a **Port/Adapter pattern** to keep UI components fully decoupled from platform-specific APIs. Components never import `@capacitor/*` directly; they depend on an injected `FilePort` interface. Concrete implementations are registered via functional providers.
40
+ The library uses a **Port/Adapter pattern** to keep UI components fully decoupled from platform-specific APIs. Components never import `@capacitor/*` directly; they depend on injected interface implementations registered via functional providers.
18
41
 
19
42
  ---
20
43
 
@@ -22,7 +45,7 @@ The library uses a **Port/Adapter pattern** to keep UI components fully decouple
22
45
 
23
46
  | Package | Description |
24
47
  |---|---|
25
- | `@keepui/ui` | Core components, ports, tokens, web adapter |
48
+ | `@keepui/ui` | Core components, ports, tokens, services, web adapter |
26
49
  | `@keepui/ui/capacitor` | Capacitor adapter — use *instead of* the web provider |
27
50
 
28
51
  ---
@@ -46,17 +69,16 @@ npx cap sync
46
69
 
47
70
  ---
48
71
 
49
- ## Usage — Angular Web App
72
+ ## Setup
50
73
 
51
74
  ### 1. Import styles
52
75
 
53
- KeepUI components use CSS custom properties for theming. You **must** import the
54
- library styles in your global stylesheet.
76
+ KeepUI components use CSS custom properties for theming. Import the library styles in your global stylesheet **before** any component-level styles.
55
77
 
56
78
  **If your project uses Tailwind CSS v4:**
57
79
 
58
80
  ```css
59
- /* src/styles.css or src/tailwind.css */
81
+ /* src/styles.css */
60
82
  @import "tailwindcss";
61
83
  @import "@keepui/ui/styles";
62
84
  ```
@@ -68,137 +90,311 @@ library styles in your global stylesheet.
68
90
  @import "@keepui/ui/styles/prebuilt.css";
69
91
  ```
70
92
 
71
- ### 2. Register the web provider
93
+ ### 2. Register providers
94
+
95
+ Register one platform provider plus the i18n provider in `app.config.ts`.
96
+
97
+ **Web:**
72
98
 
73
99
  ```ts
74
100
  // src/app/app.config.ts
75
101
  import { ApplicationConfig } from '@angular/core';
76
- import { provideKeepUi } from '@keepui/ui';
102
+ import { provideRouter } from '@angular/router';
103
+ import { provideKeepUi, provideKeepUiI18n } from '@keepui/ui';
77
104
 
78
105
  export const appConfig: ApplicationConfig = {
79
106
  providers: [
107
+ provideRouter(routes),
80
108
  provideKeepUi(),
81
- // ...other providers
109
+ provideKeepUiI18n({ defaultLang: 'en' }),
82
110
  ],
83
111
  };
84
112
  ```
85
113
 
86
- ### 3. Use components
87
-
88
- ```ts
89
- // src/app/app.component.ts
90
- import { Component } from '@angular/core';
91
- import {
92
- ButtonComponent,
93
- CardComponent,
94
- ImagePreviewComponent,
95
- } from '@keepui/ui';
96
-
97
- @Component({
98
- selector: 'app-root',
99
- standalone: true,
100
- imports: [ButtonComponent, CardComponent, ImagePreviewComponent],
101
- template: `
102
- <keepui-card>
103
- <h2>Image Picker</h2>
104
- <keepui-image-preview />
105
- </keepui-card>
106
-
107
- <keepui-button (clicked)="greet()">Hello</keepui-button>
108
- `,
109
- })
110
- export class AppComponent {
111
- greet() {
112
- alert('Hello from KeepUI!');
113
- }
114
- }
115
- ```
116
-
117
- ---
118
-
119
- ## Usage — Angular + Capacitor App
120
-
121
- ### 1. Import styles (same as web)
122
-
123
- ```css
124
- /* src/styles.css or src/tailwind.css */
125
- @import "tailwindcss";
126
- @import "@keepui/ui/styles";
127
- ```
128
-
129
- ### 2. Register the Capacitor provider
114
+ **Angular + Capacitor:**
130
115
 
131
116
  ```ts
132
117
  // src/app/app.config.ts
133
118
  import { ApplicationConfig } from '@angular/core';
119
+ import { provideRouter } from '@angular/router';
120
+ import { provideKeepUiI18n } from '@keepui/ui';
134
121
  import { provideKeepUiCapacitor } from '@keepui/ui/capacitor';
135
122
 
136
123
  export const appConfig: ApplicationConfig = {
137
124
  providers: [
138
- provideKeepUiCapacitor(),
139
- // ...other providers
125
+ provideRouter(routes),
126
+ provideKeepUiCapacitor(), // instead of provideKeepUi()
127
+ provideKeepUiI18n({ defaultLang: 'en' }),
140
128
  ],
141
129
  };
142
130
  ```
143
131
 
144
132
  > **Note:** Use `provideKeepUiCapacitor()` **instead of** `provideKeepUi()` — never both.
145
133
 
146
- ### 2. Use the same components
147
-
148
- ```ts
149
- // src/app/app.component.ts — IDENTICAL to the web example above
150
- import { ImagePreviewComponent } from '@keepui/ui';
151
- ```
152
-
153
- The component is unchanged. Only the provider differs.
154
-
155
134
  ---
156
135
 
157
136
  ## Available Components
158
137
 
159
138
  ### `<keepui-button>`
160
139
 
140
+ Accessible, themed action button with variants, shapes, sizes, loading state, full-width layout, and named icon slots.
141
+
161
142
  ```html
162
- <keepui-button (clicked)="doSomething()">Click me</keepui-button>
163
- <keepui-button [disabled]="isLoading" type="submit">Submit</keepui-button>
143
+ <!-- Basic -->
144
+ <keepui-button (clicked)="save()">Save</keepui-button>
145
+
146
+ <!-- Variant + shape -->
147
+ <keepui-button variant="danger" shape="rounded" size="auto">Delete</keepui-button>
148
+
149
+ <!-- With leading icon -->
150
+ <keepui-button variant="primary" size="auto">
151
+ <svg slot="leading" width="16" height="16" aria-hidden="true">…</svg>
152
+ Upload
153
+ </keepui-button>
154
+
155
+ <!-- Loading state -->
156
+ <keepui-button [loading]="isSaving()">Saving…</keepui-button>
157
+
158
+ <!-- Full-width -->
159
+ <keepui-button [fullWidth]="true">Submit</keepui-button>
160
+
161
+ <!-- Icon-only (ariaLabel required) -->
162
+ <keepui-button variant="ghost" size="auto" ariaLabel="Close dialog">
163
+ <svg slot="leading" …>…</svg>
164
+ </keepui-button>
164
165
  ```
165
166
 
167
+ **Inputs:**
168
+
166
169
  | Input | Type | Default | Description |
167
170
  |---|---|---|---|
168
- | `disabled` | `boolean` | `false` | Disables the button |
169
- | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | HTML type attribute |
171
+ | `variant` | `'primary' \| 'secondary' \| 'outline' \| 'ghost' \| 'danger'` | `'primary'` | Visual style. |
172
+ | `size` | `'md' \| 'auto'` | `'md'` | `md`: 160 px wide, 40 px tall. `auto`: padding-driven width. |
173
+ | `shape` | `'pill' \| 'rounded'` | `'pill'` | Border-radius style. |
174
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | HTML `type` attribute. |
175
+ | `disabled` | `boolean` | `false` | Disables the button. |
176
+ | `loading` | `boolean` | `false` | Shows spinner, disables button, sets `aria-busy="true"`. |
177
+ | `fullWidth` | `boolean` | `false` | Expands the button to 100% container width. |
178
+ | `ariaLabel` | `string` | `''` | Accessible label. Required for icon-only buttons. |
179
+
180
+ **Outputs:**
170
181
 
171
182
  | Output | Type | Description |
172
183
  |---|---|---|
173
- | `clicked` | `void` | Emitted on click |
184
+ | `clicked` | `void` | Emitted on click (only when enabled and not loading). |
185
+
186
+ **Slots:**
187
+
188
+ | Slot | Description |
189
+ |---|---|
190
+ | `slot="leading"` | Element placed before the label (e.g. icon). Hidden during loading. |
191
+ | `slot="trailing"` | Element placed after the label (e.g. icon). Hidden during loading. |
192
+ | *(default)* | Button label / content. |
193
+
194
+ ---
174
195
 
175
196
  ### `<keepui-card>`
176
197
 
198
+ Versatile container with variant, padding, color, clickable, selected, and scrollable states.
199
+
177
200
  ```html
178
- <keepui-card [elevation]="2">
179
- <p>Card content</p>
201
+ <!-- Basic -->
202
+ <keepui-card>Content</keepui-card>
203
+
204
+ <!-- Outlined with large padding -->
205
+ <keepui-card variant="outlined" padding="lg">…</keepui-card>
206
+
207
+ <!-- Clickable + selectable -->
208
+ <keepui-card [clickable]="true" [selected]="isSelected()" (clicked)="select()">
209
+ Option A
180
210
  </keepui-card>
211
+
212
+ <!-- Full-height scrollable panel -->
213
+ <div class="h-screen overflow-hidden">
214
+ <keepui-card padding="screen" [scrollable]="true" [fullHeight]="true">
215
+ Long list content…
216
+ </keepui-card>
217
+ </div>
218
+ ```
219
+
220
+ > `padding="screen"` applies lateral and top padding but omits the bottom intentionally so content appears to continue beyond the visible area. A spacer `<div>` is inserted automatically at the end of the projected content — when the user scrolls to the bottom, the correct bottom gap appears without extra markup from the consumer.
221
+
222
+ **Inputs:**
223
+
224
+ | Input | Type | Default | Description |
225
+ |---|---|---|---|
226
+ | `variant` | `'flat' \| 'outlined'` | `'outlined'` | With or without border. |
227
+ | `padding` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'screen'` | `'md'` | Internal padding. |
228
+ | `colors` | `'primary' \| 'secondary'` | `'primary'` | Background surface token. |
229
+ | `clickable` | `boolean` | `false` | Enables hover, focus ring, and button role. |
230
+ | `selected` | `boolean` | `false` | Applies brand border when active. |
231
+ | `scrollable` | `boolean` | `false` | Activates `overflow-y-auto`. Combine with `fullHeight`. |
232
+ | `fullHeight` | `boolean` | `false` | Applies `h-full` to host and inner container. |
233
+
234
+ **Outputs:**
235
+
236
+ | Output | Type | Description |
237
+ |---|---|---|
238
+ | `clicked` | `void` | Emitted on click or Enter / Space (requires `clickable`). |
239
+
240
+ ---
241
+
242
+ ### `<keepui-icon>`
243
+
244
+ Renders an SVG symbol via `<use href="#name">`. The consuming application is responsible for registering SVG symbols in the DOM (e.g. with an `IconRegistryService`).
245
+
246
+ The icon color is inherited from `currentColor` — apply any Tailwind text-color class directly to `keepui-icon`.
247
+
248
+ ```html
249
+ <!-- Decorative — aria-hidden="true" applied automatically -->
250
+ <keepui-icon name="check-icon" [size]="20" />
251
+
252
+ <!-- Semantic standalone icon — role="img" + aria-label applied -->
253
+ <keepui-icon name="close-icon" ariaLabel="Close dialog" />
254
+
255
+ <!-- Inside a button slot -->
256
+ <keepui-button variant="primary" size="auto">
257
+ <keepui-icon slot="leading" name="add-icon" [size]="16" />
258
+ New item
259
+ </keepui-button>
260
+
261
+ <!-- Custom color -->
262
+ <keepui-icon name="star-icon" [size]="24" class="text-ku-action-primary" />
181
263
  ```
182
264
 
265
+ **Inputs:**
266
+
183
267
  | Input | Type | Default | Description |
184
268
  |---|---|---|---|
185
- | `elevation` | `0 \| 1 \| 2 \| 3` | `1` | Shadow depth |
269
+ | `name` | `string` | | ID of the SVG symbol (without `#`). Required. |
270
+ | `size` | `number` | `24` | Width and height of the icon in pixels. |
271
+ | `viewBox` | `string` | `'0 0 24 24'` | `viewBox` attribute forwarded to the `<svg>` element. |
272
+ | `ariaLabel` | `string` | `''` | When provided: sets `role="img"` and `aria-label`. When omitted: `aria-hidden="true"` is applied automatically. |
273
+
274
+ > ★ Required input.
275
+
276
+ ---
277
+
278
+ ### `<keepui-icon-action-button>`
279
+
280
+ Circular icon-only action button with `default` and `danger` variants, loading state, and full accessibility support.
281
+
282
+ `ariaLabel` is **required** because the button contains no visible text (WCAG 2.1 SC 4.1.2). The icon color is inherited automatically via `currentColor` from the button's text color.
283
+
284
+ ```html
285
+ <!-- Default variant -->
286
+ <keepui-icon-action-button icon="edit-icon" ariaLabel="Edit" />
287
+
288
+ <!-- Danger variant -->
289
+ <keepui-icon-action-button
290
+ icon="trash-icon"
291
+ variant="danger"
292
+ ariaLabel="Delete item"
293
+ />
294
+
295
+ <!-- Loading state -->
296
+ <keepui-icon-action-button
297
+ icon="upload-icon"
298
+ ariaLabel="Upload file"
299
+ [loading]="isUploading()"
300
+ />
301
+
302
+ <!-- Disabled -->
303
+ <keepui-icon-action-button icon="share-icon" ariaLabel="Share" [disabled]="true" />
304
+ ```
305
+
306
+ **Inputs:**
307
+
308
+ | Input | Type | Default | Description |
309
+ |---|---|---|---|
310
+ | `icon` ★ | `string` | — | ID of the SVG symbol (without `#`). Required. |
311
+ | `ariaLabel` ★ | `string` | — | Accessible label. Required (no visible text). |
312
+ | `variant` | `'default' \| 'danger'` | `'default'` | Visual style. |
313
+ | `iconSize` | `number` | `20` | Size of the inner icon in pixels. |
314
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | HTML `type` attribute. |
315
+ | `disabled` | `boolean` | `false` | Disables the button. |
316
+ | `loading` | `boolean` | `false` | Shows spinner, disables button, sets `aria-busy="true"`. |
317
+
318
+ > ★ Required input.
319
+
320
+ ---
186
321
 
187
322
  ### `<keepui-image-preview>`
188
323
 
324
+ Standalone image picker and preview. Delegates file selection to the registered `FilePort` implementation — works identically on web and native (Capacitor) without any code change. UI strings are fully internationalised via Transloco.
325
+
189
326
  ```html
190
327
  <keepui-image-preview />
191
328
  ```
192
329
 
193
- No inputs. Requires `FILE_PORT` to be provided (via `provideKeepUi()` or `provideKeepUiCapacitor()`).
194
-
195
- **Exposed signals (for advanced usage):**
330
+ No inputs or outputs. Exposes three readable signals for advanced use cases:
196
331
 
197
332
  | Signal | Type | Description |
198
333
  |---|---|---|
199
- | `imageUrl` | `Signal<string \| null>` | URL of the selected image |
200
- | `error` | `Signal<string \| null>` | Error message if pick failed |
201
- | `loading` | `Signal<boolean>` | True while picking is in progress |
334
+ | `imageUrl` | `Signal<string \| null>` | Data URL of the selected image, ready to bind to `[src]`. |
335
+ | `error` | `Signal<string \| null>` | Error message if the last pick operation failed. |
336
+ | `loading` | `Signal<boolean>` | `true` while the pick operation is in progress. |
337
+
338
+ **Prerequisites** — register in `app.config.ts`:
339
+
340
+ ```ts
341
+ // Web
342
+ provideKeepUi()
343
+ provideKeepUiI18n({ defaultLang: 'en' })
344
+
345
+ // Capacitor
346
+ provideKeepUiCapacitor()
347
+ provideKeepUiI18n({ defaultLang: 'en' })
348
+ ```
349
+
350
+ ---
351
+
352
+ ## i18n
353
+
354
+ KeepUI uses [`@jsverse/transloco`](https://jsverse.github.io/transloco/) with the scope `'keepui'`.
355
+
356
+ **Supported languages:** `en` · `es` · `de`
357
+
358
+ **Change locale at runtime:**
359
+
360
+ ```ts
361
+ import { KeepUiLanguageService, KeepUiLanguage } from '@keepui/ui';
362
+
363
+ @Component({ … })
364
+ export class MyComponent {
365
+ private readonly langService = inject(KeepUiLanguageService);
366
+
367
+ switch(lang: KeepUiLanguage) {
368
+ this.langService.setLanguage(lang);
369
+ }
370
+ }
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Theming
376
+
377
+ KeepUI uses CSS custom properties for all design tokens. The `data-theme` attribute on `<html>` switches between `light` (default) and `dark`.
378
+
379
+ **Switch theme at runtime:**
380
+
381
+ ```ts
382
+ document.documentElement.setAttribute('data-theme', 'dark');
383
+ ```
384
+
385
+ **Override tokens in your own CSS:**
386
+
387
+ ```css
388
+ :root {
389
+ --ku-action-primary: #6366f1;
390
+ }
391
+
392
+ [data-theme="dark"] {
393
+ --ku-action-primary: #818cf8;
394
+ }
395
+ ```
396
+
397
+ Refer to the bundled `themes.css` file for the full list of available tokens.
202
398
 
203
399
  ---
204
400
 
@@ -213,10 +409,10 @@ ImagePreviewComponent
213
409
 
214
410
  FilePort (interface)
215
411
 
216
- ┌───┴───────────┐
217
-
218
- WebFileService CapacitorFileService
219
- (browser input) (@capacitor/camera)
412
+ ┌───┴──────────────────┐
413
+
414
+ WebFileService CapacitorFileService
415
+ (browser <input>) (@capacitor/camera)
220
416
  ```
221
417
 
222
418
  - Components depend only on the `FilePort` interface via the `FILE_PORT` injection token.
@@ -226,17 +422,28 @@ WebFileService CapacitorFileService
226
422
 
227
423
  ### Secondary Entrypoint
228
424
 
229
- The `@keepui/ui/capacitor` entrypoint is a secondary ng-packagr entrypoint within the same library project. It is compiled and published alongside the main package but kept in a separate entry so Capacitor dependencies are not required in web-only applications.
425
+ `@keepui/ui/capacitor` is a secondary ng-packagr entrypoint compiled alongside the main package. Keeping it separate means Capacitor dependencies are never included in web-only builds.
426
+
427
+ ### Types
428
+
429
+ Every component that exposes custom TypeScript types places those types in a sibling `<component>.types.ts` file and re-exports them from the main entrypoint. Import them directly from `@keepui/ui`:
430
+
431
+ ```ts
432
+ import {
433
+ ButtonVariant, ButtonSize, ButtonShape, ButtonType,
434
+ CardVariant, CardPadding, CardColors,
435
+ IconActionButtonVariant, IconActionButtonType,
436
+ } from '@keepui/ui';
437
+ ```
230
438
 
231
439
  ---
232
440
 
233
441
  ## Testing
234
442
 
235
- Use `MockFileService` in your tests:
443
+ Use `MockFileService` to test components that depend on `FILE_PORT`:
236
444
 
237
445
  ```ts
238
- import { MockFileService } from '@keepui/ui';
239
- import { FILE_PORT } from '@keepui/ui';
446
+ import { MockFileService, FILE_PORT } from '@keepui/ui';
240
447
 
241
448
  TestBed.configureTestingModule({
242
449
  imports: [ImagePreviewComponent],
@@ -244,7 +451,7 @@ TestBed.configureTestingModule({
244
451
  });
245
452
  ```
246
453
 
247
- `MockFileService` resolves by default. Set `nextError` to test error paths:
454
+ `MockFileService` resolves successfully by default. Set `nextError` to test error paths:
248
455
 
249
456
  ```ts
250
457
  const mock = TestBed.inject(FILE_PORT) as MockFileService;
@@ -256,20 +463,20 @@ mock.nextError = new Error('Camera cancelled');
256
463
  ## Building
257
464
 
258
465
  ```bash
259
- # Build for production (APF, partial compilation):
466
+ # Production build (APF, partial compilation):
260
467
  npm run build
261
468
 
262
- # Build for development (without schematics):
469
+ # Development build (without schematics):
263
470
  npm run build:dev
264
471
 
265
- # Build schematics only:
472
+ # Schematics only:
266
473
  npm run build:schematics
267
474
 
268
- # Run tests:
475
+ # Run unit tests:
269
476
  npm test
270
477
  ```
271
478
 
272
- The production build output is placed in `dist/keep-ui/` and follows Angular Package Format.
479
+ Output is placed in `dist/keep-ui/` following Angular Package Format.
273
480
 
274
481
  ---
275
482
 
@@ -283,42 +490,3 @@ npm publish --access public
283
490
 
284
491
  Ensure `package.json` has the correct `name`, `version`, and `peerDependencies` before publishing.
285
492
 
286
- ---
287
-
288
- ## Theming
289
-
290
- KeepUI uses CSS custom properties for all design tokens. Override any variable
291
- in your own CSS to create a custom theme:
292
-
293
- ```css
294
- :root {
295
- --keepui-primary: #6366f1;
296
- --keepui-primary-hover: #4f46e5;
297
- }
298
-
299
- [data-theme="dark"] {
300
- --keepui-primary: #818cf8;
301
- --keepui-primary-hover: #a5b4fc;
302
- }
303
- ```
304
-
305
- Switch themes at runtime:
306
-
307
- ```ts
308
- document.documentElement.setAttribute('data-theme', 'dark');
309
- ```
310
-
311
- The library also respects `prefers-color-scheme: dark` by default.
312
-
313
- See `themes.css` for the full list of available tokens.
314
-
315
- ---
316
-
317
- ## Future Improvements
318
-
319
- - Additional components (modal, toast, form controls, etc.)
320
- - More Capacitor adapters (Geolocation, Share, Haptics)
321
- - Theming support via CSS custom properties
322
- - Storybook integration
323
- - Automated publishing via GitHub Actions
324
- - E2E tests with Playwright