@ahrowe/ui 0.2.9 → 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.
Files changed (45) hide show
  1. package/README.md +5 -1
  2. package/dist/index.cjs +4 -4
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.mjs +1887 -1650
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/dist/types/package/common/avatar/avatar.d.ts +4 -0
  8. package/dist/types/package/common/avatar/avatar.types.d.ts +26 -0
  9. package/dist/types/package/common/avatar/avatarGroup.d.ts +4 -0
  10. package/dist/types/package/common/avatar/index.d.ts +3 -0
  11. package/dist/types/package/common/badge/badge.d.ts +4 -0
  12. package/dist/types/package/common/badge/badge.types.d.ts +31 -0
  13. package/dist/types/package/common/badge/index.d.ts +2 -0
  14. package/dist/types/package/common/klipyPicker/components/gifPreview/gifPreview.types.d.ts +7 -0
  15. package/dist/types/package/common/klipyPicker/index.d.ts +2 -0
  16. package/dist/types/package/common/klipyPicker/klipyPicker.d.ts +3 -0
  17. package/dist/types/package/common/klipyPicker/klipyPicker.types.d.ts +8 -0
  18. package/dist/types/package/common/skeleton/index.d.ts +2 -0
  19. package/dist/types/package/common/skeleton/skeleton.d.ts +4 -0
  20. package/dist/types/package/common/skeleton/skeleton.types.d.ts +22 -0
  21. package/dist/types/package/common/switch/index.d.ts +2 -0
  22. package/dist/types/package/common/switch/switch.d.ts +4 -0
  23. package/dist/types/package/common/switch/switch.types.d.ts +19 -0
  24. package/dist/types/package/common/themeProvider/theme.types.d.ts +8 -0
  25. package/dist/types/package/index.d.ts +10 -2
  26. package/dist/types/package/services/{tenorService.d.ts → klipy.d.ts} +9 -9
  27. package/docs/Avatar.md +62 -0
  28. package/docs/Badge.md +52 -0
  29. package/docs/Button.md +21 -0
  30. package/docs/CLAUDE.md +5 -1
  31. package/docs/{TenorPicker.md → KlipyPicker.md} +11 -11
  32. package/docs/Skeleton.md +50 -0
  33. package/docs/Switch.md +50 -0
  34. package/docs/ThemeProvider.md +1 -0
  35. package/package.json +1 -1
  36. package/dist/types/package/common/tenorPicker/components/gifPreview/gifPreview.types.d.ts +0 -7
  37. package/dist/types/package/common/tenorPicker/index.d.ts +0 -2
  38. package/dist/types/package/common/tenorPicker/tenorPicker.d.ts +0 -3
  39. package/dist/types/package/common/tenorPicker/tenorPicker.types.d.ts +0 -8
  40. package/dist/types/package/services/tenor.service.d.ts +0 -2
  41. /package/dist/types/package/common/{tenorPicker → klipyPicker}/components/gifPreview/gifPreview.d.ts +0 -0
  42. /package/dist/types/package/common/{tenorPicker → klipyPicker}/components/gifPreview/index.d.ts +0 -0
  43. /package/dist/types/package/common/{tenorPicker → klipyPicker}/components/gifView/gifView.d.ts +0 -0
  44. /package/dist/types/package/common/{tenorPicker → klipyPicker}/components/gifView/gifView.types.d.ts +0 -0
  45. /package/dist/types/package/common/{tenorPicker → klipyPicker}/components/gifView/index.d.ts +0 -0
package/docs/Avatar.md ADDED
@@ -0,0 +1,62 @@
1
+ # Avatar
2
+
3
+ **When to use:** User or entity image — profile pictures, comment authors, member lists. Falls back to initials (derived from `name`) or an icon when there's no image, or the image fails to load. Use `AvatarGroup` to stack several with overlap and a `+N` overflow indicator.
4
+
5
+ **Import:** `import { Avatar, AvatarGroup } from '@ahrowe/ui'`
6
+
7
+ **Theming:** shape defaults to `var(--default-border-radius)`. Set `--avatar-radius` (per `Avatar`/`AvatarGroup` via `style`, or theme-wide via `ThemeProvider`'s `variables`) to override it — `'50%'` for fully circular avatars is the most common case. See [ThemeProvider.md](ThemeProvider.md).
8
+
9
+ ```tsx
10
+ import { Avatar, AvatarGroup } from '@ahrowe/ui';
11
+
12
+ // Image, with initials/icon fallback if it 404s
13
+ <Avatar src="https://example.com/avatar.jpg" name="Jane Doe" />
14
+
15
+ // No image — shows initials
16
+ <Avatar name="Jane Doe" />
17
+
18
+ // No image, no name — shows a default person icon (or pass a custom `icon`)
19
+ <Avatar />
20
+
21
+ // Status dot
22
+ <Avatar name="Jane Doe" status="online" />
23
+
24
+ // Resize — sizing is `em`-based, so font-size scales the whole avatar
25
+ <Avatar name="Jane Doe" style={{ fontSize: '32px' }} />
26
+
27
+ // Fully circular instead of the default --default-border-radius — set on one Avatar,
28
+ // or on an AvatarGroup to cascade to every avatar inside (and its separator ring)
29
+ <Avatar name="Jane Doe" style={{ '--avatar-radius': '50%' }} />
30
+
31
+ // Group with overlap, capped with a "+N" overflow indicator
32
+ <AvatarGroup max={3}>
33
+ <Avatar name="Jane Doe" />
34
+ <Avatar name="Carlos Diaz" />
35
+ <Avatar name="Amy Lin" />
36
+ <Avatar name="Sam Patel" />
37
+ </AvatarGroup>
38
+ ```
39
+
40
+ **Avatar props:**
41
+
42
+ | Prop | Type | Description |
43
+ |------|------|-------------|
44
+ | `src` | `string` | Image URL |
45
+ | `alt` | `string` | Image alt text (falls back to `name`) |
46
+ | `name` | `string` | Used to derive fallback initials, and as `alt` text when `alt` isn't given |
47
+ | `icon` | `IconDefinition` | FontAwesome icon shown when there's no `src` and no `name` (default a person icon) |
48
+ | `content` | `ReactNode` | Custom fallback content overriding the computed initials/icon (e.g. `"+3"` — what `AvatarGroup` uses for its overflow indicator) |
49
+ | `status` | `'online' \| 'away' \| 'busy' \| 'offline'` | Small status dot on the bottom-right corner, rendered via `Badge` |
50
+ | `className` / `style` | | Root element — sizing is `em`-based, so `style={{ fontSize }}` scales the whole avatar. Shape defaults to `var(--default-border-radius)`; override via the `--avatar-radius` custom property (e.g. `'50%'` for fully circular) |
51
+
52
+ **AvatarGroup props:**
53
+
54
+ | Prop | Type | Description |
55
+ |------|------|-------------|
56
+ | `children` | `ReactNode` | `Avatar` elements to stack |
57
+ | `max` | `number` | Cap on visible avatars before collapsing the rest into a `+N` indicator |
58
+
59
+ The `+N` overflow indicator is itself an `Avatar` (with `content="+N"`), so it always matches whatever the avatar circle looks like. Setting `--avatar-radius` on `AvatarGroup` (rather than per-`Avatar`) cascades to every avatar inside *and* their separator rings, so the shape stays consistent across the group.
60
+
61
+ **Slots (Avatar):** `image` `fallback` `status`
62
+ **Slots (AvatarGroup):** `item` (wrapper around each visible avatar) `overflow` (wrapper around the `+N` avatar)
package/docs/Badge.md ADDED
@@ -0,0 +1,52 @@
1
+ # Badge
2
+
3
+ **When to use:** Small count or status indicator — unread counts, notification dots, "new" markers. Use standalone, or wrap an icon/avatar/button to overlay the badge on one of its corners.
4
+
5
+ **Import:** `import { Badge, BadgeStyleType, BadgePosition } from '@ahrowe/ui'`
6
+
7
+ **Style variants:** `BadgeStyleType.Default` | `BadgeStyleType.Primary` (default) | `BadgeStyleType.Success` | `BadgeStyleType.Info` | `BadgeStyleType.Warn` | `BadgeStyleType.Error`
8
+
9
+ **Position (when wrapping `children`):** `BadgePosition.TopRight` (default) | `BadgePosition.TopLeft` | `BadgePosition.BottomRight` | `BadgePosition.BottomLeft`
10
+
11
+ ```tsx
12
+ import { Badge, BadgeStyleType } from '@ahrowe/ui';
13
+ import { faBell } from '@fortawesome/free-solid-svg-icons';
14
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
15
+
16
+ // Standalone count
17
+ <Badge count={3} />
18
+
19
+ // Caps at `max` and shows "99+"
20
+ <Badge count={128} max={99} />
21
+
22
+ // Custom content instead of a count
23
+ <Badge content="NEW" />
24
+
25
+ // Plain dot, no text
26
+ <Badge dot styleType={BadgeStyleType.Error} />
27
+
28
+ // Overlaying an icon
29
+ <Badge count={12} styleType={BadgeStyleType.Error}>
30
+ <FontAwesomeIcon icon={faBell} />
31
+ </Badge>
32
+
33
+ // Hidden at 0 by default — opt in to show it
34
+ <Badge count={0} showZero>
35
+ <FontAwesomeIcon icon={faBell} />
36
+ </Badge>
37
+ ```
38
+
39
+ **Key props:**
40
+
41
+ | Prop | Type | Description |
42
+ |------|------|-------------|
43
+ | `count` | `number` | Number to display. Hidden when `0` unless `showZero` is set |
44
+ | `max` | `number` | Highest number shown before collapsing to `${max}+` (default `99`) |
45
+ | `content` | `ReactNode` | Custom content overriding the computed `count` text |
46
+ | `dot` | `boolean` | Render as a plain dot with no text |
47
+ | `showZero` | `boolean` | Show the badge when `count` is `0` |
48
+ | `styleType` | `BadgeStyleType` | Colour variant (default `Primary`) |
49
+ | `position` | `BadgePosition` | Corner the badge attaches to when wrapping `children` (default `TopRight`) |
50
+ | `children` | `ReactNode` | Element the badge overlays. Omit to render the badge inline on its own |
51
+
52
+ **Slots:** `badge`
package/docs/Button.md CHANGED
@@ -49,4 +49,25 @@ import { faSave } from '@fortawesome/free-solid-svg-icons';
49
49
  | `disabled` | `boolean` | |
50
50
  | `onClick` | `(e: MouseEvent) => void \| Promise<void>` | |
51
51
 
52
+ **Theming (default style):** the `Default` style is transparent text-coloured by default. Override it theme-wide (or per button via `style`) without touching `Primary`/`Delete`:
53
+
54
+ - `--button-default-background` — fill (default transparent)
55
+ - `--button-default-color` — text colour (default `var(--text-color)`)
56
+ - `--button-default-border` — full `border` shorthand, e.g. `1px solid var(--border-color)` (default a transparent `1px` border)
57
+
58
+ Every button reserves a transparent `1px` border, so switching `--button-default-border` on never shifts layout or changes button height. See [ThemeProvider.md](ThemeProvider.md).
59
+
60
+ ```tsx
61
+ // Outlined default buttons across the app
62
+ const theme: Theme = {
63
+ id: 'brand',
64
+ variables: { '--button-default-border': '1px solid var(--border-color)' },
65
+ };
66
+
67
+ // Or one-off via style
68
+ <Button style={{ '--button-default-border': '1px solid var(--border-color)' } as React.CSSProperties}>
69
+ Cancel
70
+ </Button>
71
+ ```
72
+
52
73
  **Slots:** `background` `content` `loadingContainer` `label`
package/docs/CLAUDE.md CHANGED
@@ -95,6 +95,8 @@ Slot keys per component are documented in each component's doc file below.
95
95
  @Accordion.md
96
96
  @ActionButtons.md
97
97
  @ActionIcon.md
98
+ @Avatar.md
99
+ @Badge.md
98
100
  @BodyEnd.md
99
101
  @Button.md
100
102
  @Card.md
@@ -118,6 +120,7 @@ Slot keys per component are documented in each component's doc file below.
118
120
  @InputDropdown.md
119
121
  @InteractableDiv.md
120
122
  @KanbanBoard.md
123
+ @KlipyPicker.md
121
124
  @Loading.md
122
125
  @Modal.md
123
126
  @NumberInput.md
@@ -129,10 +132,11 @@ Slot keys per component are documented in each component's doc file below.
129
132
  @RoomViewer.md
130
133
  @ScrollbarProvider.md
131
134
  @SearchInput.md
135
+ @Skeleton.md
132
136
  @Stepper.md
133
137
  @Sticky.md
138
+ @Switch.md
134
139
  @TabHeader.md
135
- @TenorPicker.md
136
140
  @Textarea.md
137
141
  @ThemeProvider.md
138
142
  @TimeInput.md
@@ -1,17 +1,17 @@
1
- # TenorPicker
1
+ # KlipyPicker
2
2
 
3
- **When to use:** GIF picker backed by the Tenor API — chat reactions, message composers, content authoring tools. Renders inline by default; pass `isModal` to open inside a `Modal`.
3
+ **When to use:** GIF picker backed by the Klipy API — chat reactions, message composers, content authoring tools. Renders inline by default; pass `isModal` to open inside a `Modal`.
4
4
 
5
- **Import:** `import { TenorPicker } from '@ahrowe/ui'`
5
+ **Import:** `import { KlipyPicker } from '@ahrowe/ui'`
6
6
 
7
- **Requires:** `<div id="bodyEnd"></div>` in your HTML when `isModal` is used (Modal renders via portal). A Tenor API token — the component ships with a default key, but supply your own `token` prop for production use.
7
+ **Requires:** `<div id="bodyEnd"></div>` in your HTML when `isModal` is used (Modal renders via portal). A Klipy API token — the component ships with a default key, but supply your own `token` prop for production use.
8
8
 
9
9
  ```tsx
10
- import { TenorPicker } from '@ahrowe/ui';
10
+ import { KlipyPicker } from '@ahrowe/ui';
11
11
 
12
12
  // Inline picker
13
- <TenorPicker
14
- token={process.env.TENOR_API_KEY}
13
+ <KlipyPicker
14
+ token={process.env.KLIPY_API_KEY}
15
15
  onSelect={(preview) => {
16
16
  if (preview) {
17
17
  attachGif(preview.media_formats.gif.url);
@@ -20,8 +20,8 @@ import { TenorPicker } from '@ahrowe/ui';
20
20
  />
21
21
 
22
22
  // Inside a modal trigger
23
- <TenorPicker
24
- token={process.env.TENOR_API_KEY}
23
+ <KlipyPicker
24
+ token={process.env.KLIPY_API_KEY}
25
25
  isModal
26
26
  selected={currentGifId}
27
27
  onSelect={async (preview) => {
@@ -34,8 +34,8 @@ import { TenorPicker } from '@ahrowe/ui';
34
34
 
35
35
  | Prop | Type | Description |
36
36
  |------|------|-------------|
37
- | `token` | `string` | Tenor API key (defaults to a built-in demo key — replace in production) |
37
+ | `token` | `string` | Klipy API key (defaults to a built-in demo key — replace in production) |
38
38
  | `isModal` | `boolean` | Render the picker inside a `Modal` instead of inline |
39
39
  | `selected` | `string \| null` | Currently selected GIF id (for visual highlight) |
40
- | `onSelect` | `(preview: TenorResult \| null) => void \| Promise<void>` | Fires when a GIF is picked; `null` when cleared |
40
+ | `onSelect` | `(preview: KlipyResult \| null) => void \| Promise<void>` | Fires when a GIF is picked; `null` when cleared |
41
41
  | `className` | `string` | Root element class |
@@ -0,0 +1,50 @@
1
+ # Skeleton
2
+
3
+ **When to use:** Content-shaped loading placeholder, shown while real content is being fetched — more polished than a spinner for content-heavy pages (lists, cards, profile headers). Pick the `variant` that matches the shape of what's loading.
4
+
5
+ **Import:** `import { Skeleton, SkeletonVariant, SkeletonAnimation } from '@ahrowe/ui'`
6
+
7
+ **Style variants:** `SkeletonVariant.Text` (default) | `SkeletonVariant.Circle` | `SkeletonVariant.Rectangular`
8
+
9
+ **Animation:** `SkeletonAnimation.Shimmer` (default) | `SkeletonAnimation.Pulse` | `SkeletonAnimation.None`
10
+
11
+ ```tsx
12
+ import { Skeleton, SkeletonVariant, SkeletonAnimation } from '@ahrowe/ui';
13
+
14
+ // Text line (default variant, full-width, ~1 line of text tall)
15
+ <Skeleton />
16
+
17
+ // Multiple stacked lines — the last one renders narrower, like a paragraph
18
+ <Skeleton variant={SkeletonVariant.Text} lines={3} />
19
+
20
+ // Circle — set width/height to match what it stands in for (e.g. an Avatar)
21
+ <Skeleton variant={SkeletonVariant.Circle} style={{ width: 48, height: 48 }} />
22
+
23
+ // Rectangular block — images, cards, charts
24
+ <Skeleton variant={SkeletonVariant.Rectangular} style={{ width: '100%', height: 120 }} />
25
+
26
+ // Animation
27
+ <Skeleton variant={SkeletonVariant.Text} animation={SkeletonAnimation.Pulse} />
28
+
29
+ // Composed — a typical card placeholder
30
+ <div style={{ display: 'flex', gap: 12 }}>
31
+ <Skeleton variant={SkeletonVariant.Circle} style={{ width: 48, height: 48 }} />
32
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8 }}>
33
+ <Skeleton variant={SkeletonVariant.Text} style={{ width: '40%' }} />
34
+ <Skeleton variant={SkeletonVariant.Text} lines={2} />
35
+ </div>
36
+ </div>
37
+ ```
38
+
39
+ **Sizing:** there's no dedicated size prop — set `width`/`height` via `style`/`className` to match the content being replaced. `SkeletonVariant.Text` defaults to `height: 1em` and `width: 100%` since that's almost always what's wanted for a text placeholder.
40
+
41
+ **Key props:**
42
+
43
+ | Prop | Type | Description |
44
+ |------|------|-------------|
45
+ | `variant` | `SkeletonVariant` | Placeholder shape (default `Text`) |
46
+ | `animation` | `SkeletonAnimation` | Loading animation (default `Shimmer`); respects `prefers-reduced-motion` |
47
+ | `lines` | `number` | Stacked lines, `SkeletonVariant.Text` only (default `1`); the last line is narrower |
48
+ | `className` / `style` | | Root element |
49
+
50
+ **Slots:** `line` (each stacked line when `lines > 1`)
package/docs/Switch.md ADDED
@@ -0,0 +1,50 @@
1
+ # Switch
2
+
3
+ **When to use:** Sliding boolean toggle — settings panels, feature flags, preference lists. Same shape as `Checkbox` but visually a track + thumb; prefer `Switch` when the effect is immediate (e.g. enabling a setting) and `Checkbox` for selections inside a form that gets submitted.
4
+
5
+ **Import:** `import { Switch } from '@ahrowe/ui'`
6
+
7
+ ```tsx
8
+ import { Switch, FormValidator } from '@ahrowe/ui';
9
+
10
+ // Uncontrolled with callback
11
+ <Switch
12
+ label="Dark mode"
13
+ selected={darkMode}
14
+ onToggle={(isOn) => setDarkMode(isOn)}
15
+ />
16
+
17
+ // With FormValidator
18
+ const notifyValidator = new FormValidator(false);
19
+ <Switch label="Email notifications" formValidator={notifyValidator} />
20
+
21
+ // Disabled
22
+ <Switch label="Locked setting" selected={true} disabled />
23
+
24
+ // Resize — all dimensions are in `em`, so font-size scales the whole switch
25
+ <Switch label="Large" selected={true} onToggle={() => {}} style={{ fontSize: '20px' }} />
26
+
27
+ // Or resize just the track via the slot, independent of the label
28
+ <Switch
29
+ label="Custom track size"
30
+ selected={true}
31
+ onToggle={() => {}}
32
+ styles={{ track: { width: '3em', height: '1.5em' } }}
33
+ />
34
+ ```
35
+
36
+ **Key props:**
37
+
38
+ | Prop | Type | Description |
39
+ |------|------|-------------|
40
+ | `label` | `string` | Text beside the switch |
41
+ | `selected` | `boolean` | On/off state |
42
+ | `onToggle` | `(isToggled: boolean, event?) => void` | Change handler |
43
+ | `formValidator` | `FormValidator` | Connects to form validation |
44
+ | `disabled` | `boolean` | |
45
+ | `children` | `ReactNode` | Custom label content (overrides `label`) |
46
+ | `tabIndex` | `number` | |
47
+
48
+ **Sizing:** the track and thumb are dimensioned in `em`, so the whole switch scales with the inherited `font-size` — no dedicated size prop. Use `style={{ fontSize: ... }}` to scale everything together, or the `track`/`thumb` slots (`classNames`/`styles`) to resize just those parts.
49
+
50
+ **Slots:** `track` `thumb` `label`
@@ -69,6 +69,7 @@ interface Theme {
69
69
  | `--border-color` | Borders and dividers |
70
70
  | `--error-color` / `--success-color` / `--warn-color` / `--info-color` | Semantic colours |
71
71
  | `--default-border-radius` | Border radii |
72
+ | `--avatar-radius` | `Avatar` corner radius — falls back to `--default-border-radius` when unset (set to `50%` theme-wide for fully circular avatars) |
72
73
  | `--card-shadow` | Elevation shadows |
73
74
 
74
75
  **Key props:**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahrowe/ui",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,7 +0,0 @@
1
- import { TenorResult } from '../../../../services/tenor.service';
2
- export interface GifPreviewProps {
3
- className?: string;
4
- previewItems?: TenorResult[];
5
- onSelect?: (preview: TenorResult) => void;
6
- onLoadAdditional?: () => void;
7
- }
@@ -1,2 +0,0 @@
1
- export { default } from './tenorPicker';
2
- export * from './tenorPicker.types';
@@ -1,3 +0,0 @@
1
- import { ReactElement } from 'react';
2
- import { TenorPickerProps } from './tenorPicker.types';
3
- export default function TenorPicker({ className, token, isModal, onSelect, selected, }: TenorPickerProps): ReactElement;
@@ -1,8 +0,0 @@
1
- import { TenorResult } from '../../services/tenor.service';
2
- export interface TenorPickerProps {
3
- className?: string;
4
- token?: string;
5
- isModal?: boolean;
6
- onSelect?: (preview: TenorResult | null) => void | Promise<void>;
7
- selected?: string | null;
8
- }
@@ -1,2 +0,0 @@
1
- export { default } from './tenorService';
2
- export type { TenorResult, TenorMediaFormat } from './tenorService';