@ibis-design/svelte 0.2.0 → 0.6.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 (73) hide show
  1. package/README.md +12 -2
  2. package/dist/components/buttons/Button.svelte +106 -0
  3. package/dist/components/buttons/Button.svelte.d.ts +69 -0
  4. package/dist/components/buttons/DropdownButton.svelte +91 -0
  5. package/dist/components/buttons/DropdownButton.svelte.d.ts +33 -0
  6. package/dist/components/buttons/PillTab.svelte +84 -0
  7. package/dist/components/buttons/PillTab.svelte.d.ts +55 -0
  8. package/dist/components/buttons/button.css +193 -0
  9. package/dist/components/buttons/dropdownButton.css +82 -0
  10. package/dist/components/buttons/pillTab.css +59 -0
  11. package/dist/components/containers/Banner.svelte +73 -0
  12. package/dist/components/containers/Banner.svelte.d.ts +16 -0
  13. package/dist/components/containers/Card.svelte +34 -0
  14. package/dist/components/containers/Card.svelte.d.ts +14 -0
  15. package/dist/components/containers/PillTabs.svelte +22 -0
  16. package/dist/components/containers/PillTabs.svelte.d.ts +6 -0
  17. package/dist/components/containers/TipIndicator.svelte +78 -0
  18. package/dist/components/containers/TipIndicator.svelte.d.ts +28 -0
  19. package/dist/components/containers/Toaster.svelte +75 -0
  20. package/dist/components/containers/Toaster.svelte.d.ts +16 -0
  21. package/dist/components/containers/Tooltip.svelte.d.ts +26 -0
  22. package/dist/components/containers/banner.css +155 -0
  23. package/dist/components/containers/tipIndicator.css +79 -0
  24. package/dist/components/containers/toaster.css +137 -0
  25. package/dist/components/containers/tooltip.css +0 -0
  26. package/dist/components/inputs/.gitkeep +0 -0
  27. package/dist/components/inputs/Checkbox.svelte +95 -0
  28. package/dist/components/inputs/Checkbox.svelte.d.ts +33 -0
  29. package/dist/components/inputs/Chips.svelte +104 -0
  30. package/dist/components/inputs/Chips.svelte.d.ts +48 -0
  31. package/dist/components/inputs/Dropdown.svelte +83 -0
  32. package/dist/components/inputs/Dropdown.svelte.d.ts +15 -0
  33. package/dist/components/inputs/Radio.svelte +109 -0
  34. package/dist/components/inputs/Radio.svelte.d.ts +49 -0
  35. package/dist/components/inputs/Switch.svelte +45 -0
  36. package/dist/components/inputs/Switch.svelte.d.ts +21 -0
  37. package/dist/components/inputs/TextArea.svelte +65 -0
  38. package/dist/components/inputs/TextArea.svelte.d.ts +14 -0
  39. package/dist/components/inputs/TextInput.svelte +273 -0
  40. package/dist/components/inputs/TextInput.svelte.d.ts +140 -0
  41. package/dist/components/inputs/TextLink.svelte +102 -0
  42. package/dist/components/inputs/TextLink.svelte.d.ts +21 -0
  43. package/dist/components/inputs/checkbox.css +103 -0
  44. package/dist/components/inputs/chips.css +76 -0
  45. package/dist/components/inputs/dropdown.css +106 -0
  46. package/dist/components/inputs/radio.css +108 -0
  47. package/dist/components/inputs/switch.css +68 -0
  48. package/dist/components/inputs/textInput.css +152 -0
  49. package/dist/components/inputs/textarea.css +91 -0
  50. package/dist/components/inputs/textlink.css +163 -0
  51. package/dist/index.d.ts +21 -8
  52. package/dist/index.js +36 -2000
  53. package/dist/layouts/AppLayout.svelte +83 -0
  54. package/dist/layouts/AppLayout.svelte.d.ts +20 -0
  55. package/dist/layouts/AuthLayout.svelte +63 -0
  56. package/dist/layouts/AuthLayout.svelte.d.ts +18 -0
  57. package/dist/layouts/DashboardLayout.svelte +88 -0
  58. package/dist/layouts/DashboardLayout.svelte.d.ts +20 -0
  59. package/dist/types/button.js +5 -0
  60. package/dist/types/index.d.ts +2 -2
  61. package/dist/types/index.js +5 -0
  62. package/dist/types/layout.d.ts +1 -1
  63. package/dist/types/layout.js +1 -0
  64. package/package.json +49 -44
  65. package/dist/index.css +0 -1
  66. package/dist/index.js.map +0 -1
  67. package/dist/lib/components/buttons/Button.svelte.d.ts +0 -1
  68. package/dist/lib/components/buttons/DropdownButton.svelte.d.ts +0 -1
  69. package/dist/lib/components/containers/Card.svelte.d.ts +0 -1
  70. package/dist/lib/layouts/AppLayout.svelte.d.ts +0 -1
  71. package/dist/lib/layouts/AuthLayout.svelte.d.ts +0 -1
  72. package/dist/lib/layouts/DashboardLayout.svelte.d.ts +0 -1
  73. /package/dist/{types/input.d.ts → components/containers/Tooltip.svelte} +0 -0
@@ -0,0 +1,79 @@
1
+ .ibis-tip-indicator {
2
+ position: relative;
3
+ display: inline-flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ cursor: pointer;
7
+ color: var(--color-neutral-400);
8
+ transition: color var(--transition-duration-fast) var(--transition-timing-default);
9
+ outline: none;
10
+ }
11
+
12
+ .ibis-tip-indicator:hover,
13
+ .ibis-tip-indicator:focus-visible {
14
+ color: var(--color-primary-500);
15
+ }
16
+
17
+ .ibis-tip-indicator__icon {
18
+ width: 1em;
19
+ height: 1em;
20
+ display: block;
21
+ }
22
+
23
+ /* Tooltip box */
24
+ .ibis-tip {
25
+ position: absolute;
26
+ z-index: 100;
27
+ background-color: var(--color-neutral-600);
28
+ color: var(--color-white);
29
+ font-size: var(--font-size-normal-text-sm);
30
+ font-family: var(--font-family-sans);
31
+ padding: var(--spacing-2) var(--spacing-4);
32
+ border-radius: var(--border-radius-full);
33
+ pointer-events: none;
34
+ white-space: nowrap;
35
+ }
36
+
37
+ /* Width variants */
38
+ .ibis-tip--width-auto {
39
+ white-space: nowrap;
40
+ width: auto;
41
+ }
42
+
43
+ .ibis-tip--width-240 {
44
+ width: 240px;
45
+ white-space: normal;
46
+ }
47
+
48
+ .ibis-tip--width-360 {
49
+ width: 360px;
50
+ white-space: normal;
51
+ }
52
+
53
+ /* Top */
54
+ .ibis-tip--top {
55
+ bottom: calc(100% + 8px);
56
+ left: 50%;
57
+ transform: translateX(-50%);
58
+ }
59
+
60
+ /* Bottom */
61
+ .ibis-tip--bottom {
62
+ top: calc(100% + 8px);
63
+ left: 50%;
64
+ transform: translateX(-50%);
65
+ }
66
+
67
+ /* Left */
68
+ .ibis-tip--left {
69
+ right: calc(100% + 8px);
70
+ top: 50%;
71
+ transform: translateY(-50%);
72
+ }
73
+
74
+ /* Right */
75
+ .ibis-tip--right {
76
+ left: calc(100% + 8px);
77
+ top: 50%;
78
+ transform: translateY(-50%);
79
+ }
@@ -0,0 +1,137 @@
1
+ .ibis-toaster {
2
+ font-family: var(--font-family-sans);
3
+ display: flex;
4
+ align-items: center;
5
+ gap: 8px; /*hardcoded*/
6
+ padding: 8px 16px;
7
+ border-radius: 16px; /*hardcoded*/
8
+ border: var(--border-width-default) solid var(--color-neutral-200);
9
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
10
+
11
+ max-width: 200px;
12
+ position: relative;
13
+ margin-bottom: 2px;
14
+ }
15
+
16
+ /* ICON */
17
+ .ibis-toaster__icon {
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+
22
+ width: 20px;
23
+ height: 20px;
24
+ top: 2px;
25
+ left: 2px;
26
+
27
+ flex-shrink: 0;
28
+
29
+ margin-top: 2px;
30
+ color: var(--color-white);
31
+ }
32
+
33
+ .ibis-toaster__icon :global(svg) {
34
+ width: 100%;
35
+ height: 100%;
36
+ }
37
+
38
+ .ibis-toaster__content {
39
+ flex: 1;
40
+ display: flex;
41
+ flex-direction: column;
42
+ justify-content: center;
43
+ }
44
+
45
+ .ibis-toaster__title {
46
+ font-weight: var(--font-weight-medium);
47
+ font-size: var(--font-size-normal-text-sm);
48
+ }
49
+
50
+ .ibis-toaster__message {
51
+ font-size: var(--font-size-normal-text-sm);
52
+ color: var(--color-white);
53
+ }
54
+
55
+ .ibis-toaster--default .ibis-toaster__message {
56
+ font-size: var(--font-size-normal-text-sm);
57
+ color: var(--color-black);
58
+ }
59
+
60
+ .ibis-toaster--default .ibis-toaster__icon {
61
+ color: var(--color-black);
62
+ }
63
+
64
+ .ibis-toaster__close {
65
+ background: transparent;
66
+ border: none;
67
+ cursor: pointer;
68
+ font-size: 18px; /*hardcoded*/
69
+ line-height: 1;
70
+ color: var(--color-white);
71
+ flex-shrink: 0;
72
+ }
73
+
74
+ .ibis-toaster--default .ibis-toaster__close {
75
+ color: var(--color-black);
76
+ border-color: var(--color-white);
77
+ }
78
+
79
+ /* SUCCESS */
80
+ .ibis-toaster--success {
81
+ border-color: var(--color-status-success);
82
+ background-color: var(--color-status-success);
83
+ }
84
+
85
+ .ibis-toaster--success .ibis-toaster__title {
86
+ color: var(--color-white);
87
+ }
88
+
89
+ /* ERROR */
90
+ .ibis-toaster--error {
91
+ border-color: var(--color-status-error);
92
+ background-color: var(--color-status-error);
93
+ }
94
+
95
+ .ibis-toaster--error .ibis-toaster__title {
96
+ color: var(--color-white);
97
+ }
98
+
99
+ /* ACCENT */
100
+ .ibis-toaster--accent {
101
+ border-color: #920075; /*hardcoded*/
102
+ background-color: #920075; /*hardcoded*/
103
+ }
104
+
105
+ .ibis-toaster--accent .ibis-toaster__title {
106
+ color: var(--color-white);
107
+ }
108
+
109
+ /* DEFAULT */
110
+ .ibis-toaster--default {
111
+ border-color: var(--color-neutral-100);
112
+ background-color: var(--color-neutral-100);
113
+ }
114
+
115
+ .ibis-toaster--default .ibis-toaster__title {
116
+ color: var(--color-black);
117
+ }
118
+
119
+ .ibis-toaster__loader {
120
+ width: 1em;
121
+ height: 1em;
122
+
123
+ border: var(--border-width-default) solid var(--color-primary-300);
124
+ border-top-color: var(--color-primary-900);
125
+ border-radius: 50%;
126
+
127
+ animation: ibis-spin 0.8s linear infinite;
128
+ }
129
+
130
+ @keyframes ibis-spin {
131
+ from {
132
+ transform: rotate(0deg);
133
+ }
134
+ to {
135
+ transform: rotate(360deg);
136
+ }
137
+ }
File without changes
File without changes
@@ -0,0 +1,95 @@
1
+ <script lang="ts">
2
+ import "./checkbox.css";
3
+ import type { Snippet } from "svelte";
4
+ import type { HTMLInputAttributes } from "svelte/elements";
5
+
6
+ interface Props extends HTMLInputAttributes {
7
+ label?: string;
8
+ id?: string;
9
+ /**
10
+ * Defines whether the checkbox is checked.
11
+ *
12
+ * @default false
13
+ */
14
+ checked?: boolean;
15
+ inputSize?: "sm" | "md" | "lg";
16
+ /**
17
+ * Defines whether the checkbox is disabled.
18
+ *
19
+ * @default false
20
+ */
21
+ disabled?: boolean;
22
+ /**
23
+ * Defines whether the checkbox is in an invalid state.
24
+ *
25
+ * @default false
26
+ */
27
+ invalid?: boolean;
28
+ description?: string;
29
+ error?: string;
30
+ helpText?: string;
31
+ checkboxSnippet?: Snippet;
32
+ }
33
+
34
+ let {
35
+ id,
36
+ label,
37
+ checked = $bindable(false),
38
+ inputSize = "md",
39
+ disabled = false,
40
+ invalid = false,
41
+ description,
42
+ error,
43
+ helpText,
44
+ checkboxSnippet,
45
+ ...rest
46
+ }: Props = $props();
47
+
48
+ const fallbackId = `ibis-checkbox-${Math.random().toString(36).slice(2)}`;
49
+ const inputId = $derived(id ?? fallbackId);
50
+ </script>
51
+
52
+ <div
53
+ class="ibis-checkbox
54
+ ibis-checkbox--{inputSize}
55
+ {disabled ? 'ibis-checkbox--disabled' : ''}
56
+ {invalid ? 'ibis-checkbox--invalid' : ''}">
57
+ {#if label}
58
+ <div class="ibis-checkbox__label-wrapper">
59
+ <label class="ibis-checkbox__label" for={inputId}>{label}</label>
60
+
61
+ {#if description}
62
+ <div class="ibis-checkbox__description">{description}</div>
63
+ {/if}
64
+ </div>
65
+ {/if}
66
+
67
+ <div class="ibis-checkbox__wrapper">
68
+ <label class="ibis-checkbox__control">
69
+ <input
70
+ {...rest}
71
+ id={inputId}
72
+ type="checkbox"
73
+ bind:checked
74
+ {disabled}
75
+ aria-invalid={invalid}
76
+ class="ibis-checkbox__input" />
77
+
78
+ <span class="ibis-checkbox__box">
79
+ <span class="ibis-checkbox__icon">
80
+ {@render checkboxSnippet?.()}
81
+ </span>
82
+ </span>
83
+
84
+ {#if label}
85
+ <span class="ibis-checkbox__text">{label}</span>
86
+ {/if}
87
+ </label>
88
+ </div>
89
+
90
+ {#if invalid && error}
91
+ <div class="ibis-checkbox__error" role="alert">{error}</div>
92
+ {:else if helpText}
93
+ <div class="ibis-checkbox__help">{helpText}</div>
94
+ {/if}
95
+ </div>
@@ -0,0 +1,33 @@
1
+ import "./checkbox.css";
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLInputAttributes } from "svelte/elements";
4
+ interface Props extends HTMLInputAttributes {
5
+ label?: string;
6
+ id?: string;
7
+ /**
8
+ * Defines whether the checkbox is checked.
9
+ *
10
+ * @default false
11
+ */
12
+ checked?: boolean;
13
+ inputSize?: "sm" | "md" | "lg";
14
+ /**
15
+ * Defines whether the checkbox is disabled.
16
+ *
17
+ * @default false
18
+ */
19
+ disabled?: boolean;
20
+ /**
21
+ * Defines whether the checkbox is in an invalid state.
22
+ *
23
+ * @default false
24
+ */
25
+ invalid?: boolean;
26
+ description?: string;
27
+ error?: string;
28
+ helpText?: string;
29
+ checkboxSnippet?: Snippet;
30
+ }
31
+ declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
32
+ type Checkbox = ReturnType<typeof Checkbox>;
33
+ export default Checkbox;
@@ -0,0 +1,104 @@
1
+ <script lang="ts">
2
+ import "./chips.css";
3
+ import type { HTMLButtonAttributes } from "svelte/elements";
4
+ import type { Snippet } from "svelte";
5
+ // define props
6
+ interface Props extends HTMLButtonAttributes {
7
+ /**
8
+ * Defines the size of the chip.
9
+ *
10
+ * @default "md"
11
+ */
12
+ size?: "sm" | "md" | "lg";
13
+ /**
14
+ * Defines whether the chip is disabled.
15
+ *
16
+ * @default false
17
+ */
18
+ disabled?: boolean;
19
+ /**
20
+ * Defines whether the chip is selected.
21
+ *
22
+ * @default false
23
+ */
24
+ selected?: boolean;
25
+ /**
26
+ * Shows a loading spinner and disables the chip.
27
+ *
28
+ * @default false
29
+ */
30
+ loading?: boolean;
31
+ /**
32
+ * Disables the chip and applies skeleton styling
33
+ *
34
+ * @default false
35
+ */
36
+ skeleton?: boolean;
37
+ /**
38
+ * Plain text label for the chip. Use children snippet for complex content.
39
+ */
40
+ label?: string;
41
+ /**
42
+ * Plain text or emoji icon. Use icon snippet for complex content.
43
+ */
44
+ iconText?: string;
45
+ children?: Snippet;
46
+ icon?: Snippet<[]>;
47
+ }
48
+
49
+ let {
50
+ size = "md",
51
+ selected = $bindable(false),
52
+ disabled = false,
53
+ loading = false,
54
+ skeleton = false,
55
+ label,
56
+ iconText,
57
+ children,
58
+ icon,
59
+ ...rest
60
+ }: Props = $props();
61
+
62
+ const toggleSelected = () => {
63
+ if (disabled || loading || skeleton) return;
64
+ selected = !selected;
65
+ };
66
+ </script>
67
+
68
+ <!-- add how it would look in the DOM -->
69
+ <button
70
+ {...rest}
71
+ {disabled}
72
+ aria-pressed={selected}
73
+ aria-busy={loading}
74
+ onclick={toggleSelected}
75
+ class="ibis-chips
76
+ ibis-chips--{size}
77
+ {selected ? 'ibis-chips--selected' : ''}
78
+ {skeleton ? 'ibis-chips--skeleton' : ''}">
79
+ <span class="ibis-chips__content {skeleton ? 'ibis-chips__content--hidden' : ''}">
80
+ {#if icon || iconText}
81
+ <span class="ibis-chips__icon">
82
+ {#if icon}
83
+ {@render icon()}
84
+ {:else}
85
+ {iconText}
86
+ {/if}
87
+ </span>
88
+ {/if}
89
+
90
+ {#if children || label}
91
+ <span class="ibis-chips__label">
92
+ {#if children}
93
+ {@render children?.()}
94
+ {:else}
95
+ {label}
96
+ {/if}
97
+ </span>
98
+ {/if}
99
+ </span>
100
+
101
+ {#if loading}
102
+ <span class="ibis-chips__loader" aria-hidden="true"></span>
103
+ {/if}
104
+ </button>
@@ -0,0 +1,48 @@
1
+ import "./chips.css";
2
+ import type { HTMLButtonAttributes } from "svelte/elements";
3
+ import type { Snippet } from "svelte";
4
+ interface Props extends HTMLButtonAttributes {
5
+ /**
6
+ * Defines the size of the chip.
7
+ *
8
+ * @default "md"
9
+ */
10
+ size?: "sm" | "md" | "lg";
11
+ /**
12
+ * Defines whether the chip is disabled.
13
+ *
14
+ * @default false
15
+ */
16
+ disabled?: boolean;
17
+ /**
18
+ * Defines whether the chip is selected.
19
+ *
20
+ * @default false
21
+ */
22
+ selected?: boolean;
23
+ /**
24
+ * Shows a loading spinner and disables the chip.
25
+ *
26
+ * @default false
27
+ */
28
+ loading?: boolean;
29
+ /**
30
+ * Disables the chip and applies skeleton styling
31
+ *
32
+ * @default false
33
+ */
34
+ skeleton?: boolean;
35
+ /**
36
+ * Plain text label for the chip. Use children snippet for complex content.
37
+ */
38
+ label?: string;
39
+ /**
40
+ * Plain text or emoji icon. Use icon snippet for complex content.
41
+ */
42
+ iconText?: string;
43
+ children?: Snippet;
44
+ icon?: Snippet<[]>;
45
+ }
46
+ declare const Chips: import("svelte").Component<Props, {}, "selected">;
47
+ type Chips = ReturnType<typeof Chips>;
48
+ export default Chips;
@@ -0,0 +1,83 @@
1
+ <script lang="ts">
2
+ import "./dropdown.css";
3
+ import TextInput from "./TextInput.svelte";
4
+
5
+ interface Options {
6
+ label: string;
7
+ value: string;
8
+ }
9
+
10
+ interface Props {
11
+ label?: string;
12
+ value?: string;
13
+ options?: Options[];
14
+ placeholder?: string;
15
+ disabled?: boolean;
16
+ }
17
+
18
+ let {
19
+ label,
20
+ value = $bindable(""),
21
+ options = [],
22
+ placeholder = "Select...",
23
+ disabled = false,
24
+ }: Props = $props();
25
+
26
+ let open = $state(false);
27
+ let dropdownRef = $state<HTMLDivElement | null>(null);
28
+
29
+ const listboxId = `ibis-dropdown-listbox-${Math.random().toString(36).slice(2)}`;
30
+
31
+ const selectOption = (option: Options) => {
32
+ value = option.value;
33
+ open = false;
34
+ };
35
+
36
+ const toggleDropdown = () => {
37
+ if (!disabled) open = !open;
38
+ };
39
+
40
+ const handleKeydown = (e: KeyboardEvent) => {
41
+ if (e.key === "Escape") open = false;
42
+ };
43
+
44
+ const handleClickOutside = (e: MouseEvent) => {
45
+ if (dropdownRef && !dropdownRef.contains(e.target as Node)) {
46
+ open = false;
47
+ }
48
+ };
49
+
50
+ const selectedLabel = $derived(options.find((o) => o.value === value)?.label ?? value);
51
+ </script>
52
+
53
+ <svelte:window onclick={handleClickOutside} />
54
+
55
+ {#snippet suffixSnippet()}
56
+
57
+ {/snippet}
58
+
59
+ <div
60
+ class="ibis-dropdown {open ? 'ibis-dropdown--open' : ''}"
61
+ role="combobox"
62
+ tabindex="0"
63
+ aria-expanded={open}
64
+ aria-haspopup="listbox"
65
+ aria-controls={listboxId}
66
+ bind:this={dropdownRef}
67
+ onkeydown={handleKeydown}>
68
+ <button type="button" class="ibis-dropdown__trigger" onclick={toggleDropdown} {disabled}>
69
+ <TextInput {label} value={selectedLabel} {placeholder} {disabled} readonly {suffixSnippet} />
70
+ </button>
71
+
72
+ {#if open}
73
+ <ul id={listboxId} role="listbox" class="ibis-dropdown__menu">
74
+ {#each options as option (option.value)}
75
+ <li role="option" aria-selected={option.value === value}>
76
+ <button type="button" class="ibis-dropdown__item" onclick={() => selectOption(option)}>
77
+ {option.label}
78
+ </button>
79
+ </li>
80
+ {/each}
81
+ </ul>
82
+ {/if}
83
+ </div>
@@ -0,0 +1,15 @@
1
+ import "./dropdown.css";
2
+ interface Options {
3
+ label: string;
4
+ value: string;
5
+ }
6
+ interface Props {
7
+ label?: string;
8
+ value?: string;
9
+ options?: Options[];
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ }
13
+ declare const Dropdown: import("svelte").Component<Props, {}, "value">;
14
+ type Dropdown = ReturnType<typeof Dropdown>;
15
+ export default Dropdown;
@@ -0,0 +1,109 @@
1
+ <script lang="ts">
2
+ import "./radio.css";
3
+ import type { HTMLInputAttributes } from "svelte/elements";
4
+
5
+ interface Props extends HTMLInputAttributes {
6
+ /**
7
+ * The text label displayed next to the radio button, describing its purpose or option.
8
+ */
9
+ label?: string;
10
+ id?: string;
11
+ /**
12
+ * The value associated with the radio button.
13
+ *
14
+ * Determines which option is selected within a group of radio buttons.
15
+ *
16
+ * @default ""
17
+ */
18
+ value: string | number;
19
+ /**
20
+ * The name of the radio button group.
21
+ *
22
+ * Radio buttons with the same `name` are grouped together, allowing only one to be selected at a time.
23
+ *
24
+ * @default undefined
25
+ */
26
+ group?: string | number;
27
+ /**
28
+ * Determines the size of the radio button input.
29
+ *
30
+ * @default "md"
31
+ */
32
+ inputSize?: "sm" | "md" | "lg";
33
+ /**
34
+ * Disables the radio button, preventing user interaction and applying disabled styles.
35
+ *
36
+ * @default false
37
+ */
38
+ disabled?: boolean;
39
+ /**
40
+ * Marks the radio button as invalid, applying error styles and showing error messages if provided.
41
+ *
42
+ * @default false
43
+ */
44
+ invalid?: boolean;
45
+ description?: string;
46
+ error?: string;
47
+ helpText?: string;
48
+ }
49
+
50
+ let {
51
+ id,
52
+ label,
53
+ value,
54
+ group = $bindable(),
55
+ inputSize = "md",
56
+ disabled = false,
57
+ invalid = false,
58
+ description,
59
+ error,
60
+ helpText,
61
+ ...rest
62
+ }: Props = $props();
63
+
64
+ const fallbackId = `ibis-radio-${Math.random().toString(36).slice(2)}`;
65
+ const inputId = $derived(id ?? fallbackId);
66
+
67
+ const checked = $derived(group === value);
68
+ </script>
69
+
70
+ <div
71
+ class="ibis-radio
72
+ ibis-radio--{inputSize}
73
+ {disabled ? 'ibis-radio--disabled' : ''}
74
+ {invalid ? 'ibis-radio--invalid' : ''}">
75
+ {#if label}
76
+ <div class="ibis-radio__label-wrapper">
77
+ <label class="ibis-radio__label" for={inputId}>{label}</label>
78
+
79
+ {#if description}
80
+ <div class="ibis-radio__description">{description}</div>
81
+ {/if}
82
+ </div>
83
+ {/if}
84
+
85
+ <label class="ibis-radio__control" for={inputId}>
86
+ <input
87
+ {...rest}
88
+ id={inputId}
89
+ type="radio"
90
+ bind:group
91
+ {value}
92
+ {disabled}
93
+ class="ibis-radio__input" />
94
+
95
+ <span class="ibis-radio__circle">
96
+ <span class="ibis-radio__dot" class:ibis-radio__dot--active={checked}></span>
97
+ </span>
98
+
99
+ {#if label}
100
+ <span class="ibis-radio__text">{label}</span>
101
+ {/if}
102
+ </label>
103
+
104
+ {#if invalid && error}
105
+ <div class="ibis-radio__error" role="alert">{error}</div>
106
+ {:else if helpText}
107
+ <div class="ibis-radio__help">{helpText}</div>
108
+ {/if}
109
+ </div>