negodesign 0.0.74 → 0.0.76

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 (121) hide show
  1. package/dist/components/pages/security/auth/PageAuth01.svelte +65 -0
  2. package/dist/components/pages/security/auth/PageAuth01.svelte.d.ts +17 -0
  3. package/dist/components/pages/security/auth/PageAuth02.svelte +45 -0
  4. package/dist/components/pages/security/auth/PageAuth02.svelte.d.ts +17 -0
  5. package/dist/components/pages/security/auth/PageAuth03.svelte +45 -0
  6. package/dist/components/pages/security/auth/PageAuth03.svelte.d.ts +17 -0
  7. package/dist/components/pages/security/login/01/ui/PageLogin01.svelte +15 -40
  8. package/dist/components/pages/security/login/02/ui/LoginCard02.svelte +15 -4
  9. package/dist/components/pages/security/login/02/ui/PageLogin02.svelte +15 -20
  10. package/dist/components/pages/security/login/03/ui/LoginCard03.svelte +34 -16
  11. package/dist/components/pages/security/login/03/ui/PageLogin03.svelte +15 -20
  12. package/dist/components/pages/security/register/01/ui/PageRegister01.svelte +64 -0
  13. package/dist/components/pages/security/register/01/ui/PageRegister01.svelte.d.ts +3 -0
  14. package/dist/components/pages/security/register/01/ui/RegisterCard.svelte +100 -0
  15. package/dist/components/pages/security/register/01/ui/RegisterCard.svelte.d.ts +12 -0
  16. package/dist/components/pages/security/register/02/ui/PageRegister02.svelte +76 -0
  17. package/dist/components/pages/security/register/02/ui/PageRegister02.svelte.d.ts +4 -0
  18. package/dist/components/pages/security/register/02/ui/RegisterCard02.svelte +98 -0
  19. package/dist/components/pages/security/register/02/ui/RegisterCard02.svelte.d.ts +12 -0
  20. package/dist/components/pages/security/register/03/ui/PageRegister03.svelte +76 -0
  21. package/dist/components/pages/security/register/03/ui/PageRegister03.svelte.d.ts +4 -0
  22. package/dist/components/pages/security/register/03/ui/RegisterCard03.svelte +103 -0
  23. package/dist/components/pages/security/register/03/ui/RegisterCard03.svelte.d.ts +12 -0
  24. package/dist/components/pages/security/register/FormRegister.svelte +120 -0
  25. package/dist/components/pages/security/register/FormRegister.svelte.d.ts +8 -0
  26. package/dist/components/pages/security/register/PageRegister.svelte +30 -0
  27. package/dist/components/pages/security/register/PageRegister.svelte.d.ts +8 -0
  28. package/dist/components/pages/security/register/types.d.ts +51 -0
  29. package/dist/components/pages/security/register/types.js +1 -0
  30. package/dist/components/ui/form/Form.svelte +171 -0
  31. package/dist/components/ui/form/Form.svelte.d.ts +4 -0
  32. package/dist/components/ui/form/types.d.ts +78 -0
  33. package/dist/components/ui/form/types.js +1 -0
  34. package/dist/components/ui/form/ui/input-badges.svelte +214 -0
  35. package/dist/components/ui/form/ui/input-badges.svelte.d.ts +14 -0
  36. package/dist/components/ui/form/ui/input-checkbox.svelte +86 -0
  37. package/dist/components/ui/form/ui/input-checkbox.svelte.d.ts +17 -0
  38. package/dist/components/ui/form/ui/input-radio.svelte +82 -0
  39. package/dist/components/ui/form/ui/input-radio.svelte.d.ts +17 -0
  40. package/dist/components/ui/form/ui/input-select.svelte +195 -0
  41. package/dist/components/ui/form/ui/input-select.svelte.d.ts +17 -0
  42. package/dist/components/ui/form/ui/input-textarea.svelte +61 -0
  43. package/dist/components/ui/form/ui/input-textarea.svelte.d.ts +13 -0
  44. package/dist/components/ui/form/ui/input-toggle.svelte +89 -0
  45. package/dist/components/ui/form/ui/input-toggle.svelte.d.ts +11 -0
  46. package/dist/components/ui/modal/badge/index.d.ts +3 -0
  47. package/dist/components/ui/modal/badge/index.js +2 -0
  48. package/dist/components/ui/modal/badge/types.d.ts +35 -0
  49. package/dist/components/ui/modal/badge/types.js +1 -0
  50. package/dist/components/ui/modal/badge/ui/ModalBadgeSelection.svelte +87 -0
  51. package/dist/components/ui/modal/badge/ui/ModalBadgeSelection.svelte.d.ts +4 -0
  52. package/dist/components/ui/modal/core/index.d.ts +3 -0
  53. package/dist/components/ui/modal/core/index.js +2 -0
  54. package/dist/components/ui/modal/core/types.d.ts +84 -0
  55. package/dist/components/ui/modal/core/types.js +1 -0
  56. package/dist/components/ui/modal/core/ui/ModalCore.svelte +132 -0
  57. package/dist/components/ui/modal/core/ui/ModalCore.svelte.d.ts +4 -0
  58. package/dist/components/ui/modal/feedback/index.d.ts +3 -0
  59. package/dist/components/ui/modal/feedback/index.js +2 -0
  60. package/dist/components/ui/modal/feedback/types.d.ts +32 -0
  61. package/dist/components/ui/modal/feedback/types.js +1 -0
  62. package/dist/components/ui/modal/feedback/ui/ModalFeedback.svelte +135 -0
  63. package/dist/components/ui/modal/feedback/ui/ModalFeedback.svelte.d.ts +4 -0
  64. package/dist/components/ui/modal/form/index.d.ts +3 -0
  65. package/dist/components/ui/modal/form/index.js +2 -0
  66. package/dist/components/ui/modal/form/types.d.ts +38 -0
  67. package/dist/components/ui/modal/form/types.js +1 -0
  68. package/dist/components/ui/modal/form/ui/ModalForm.svelte +67 -0
  69. package/dist/components/ui/modal/form/ui/ModalForm.svelte.d.ts +4 -0
  70. package/dist/components/ui/modal/grid/index.d.ts +5 -0
  71. package/dist/components/ui/modal/grid/index.js +4 -0
  72. package/dist/components/ui/modal/grid/types.d.ts +51 -0
  73. package/dist/components/ui/modal/grid/types.js +1 -0
  74. package/dist/components/ui/modal/grid/ui/01/ModalGridSelection01.svelte +83 -0
  75. package/dist/components/ui/modal/grid/ui/01/ModalGridSelection01.svelte.d.ts +4 -0
  76. package/dist/components/ui/modal/grid/ui/02/ModalGridSelection02.svelte +146 -0
  77. package/dist/components/ui/modal/grid/ui/02/ModalGridSelection02.svelte.d.ts +4 -0
  78. package/dist/components/ui/modal/grid/ui/ModalGridSelection.svelte +60 -0
  79. package/dist/components/ui/modal/grid/ui/ModalGridSelection.svelte.d.ts +4 -0
  80. package/dist/components/ui/modal/map/index.d.ts +3 -0
  81. package/dist/components/ui/modal/map/index.js +2 -0
  82. package/dist/components/ui/modal/map/types.d.ts +48 -0
  83. package/dist/components/ui/modal/map/types.js +1 -0
  84. package/dist/components/ui/modal/map/ui/ModalMap.svelte +187 -0
  85. package/dist/components/ui/modal/map/ui/ModalMap.svelte.d.ts +4 -0
  86. package/dist/components/ui/modal/notification/index.d.ts +3 -0
  87. package/dist/components/ui/modal/notification/index.js +2 -0
  88. package/dist/components/ui/modal/notification/types.d.ts +26 -0
  89. package/dist/components/ui/modal/notification/types.js +1 -0
  90. package/dist/components/ui/modal/notification/ui/ModalNotification.svelte +65 -0
  91. package/dist/components/ui/modal/notification/ui/ModalNotification.svelte.d.ts +4 -0
  92. package/dist/components/ui/modal/share/index.d.ts +3 -0
  93. package/dist/components/ui/modal/share/index.js +2 -0
  94. package/dist/components/ui/modal/share/types.d.ts +47 -0
  95. package/dist/components/ui/modal/share/types.js +1 -0
  96. package/dist/components/ui/modal/share/ui/ModalShareSelection.svelte +156 -0
  97. package/dist/components/ui/modal/share/ui/ModalShareSelection.svelte.d.ts +4 -0
  98. package/dist/components/ui/modal/upload/index.d.ts +3 -0
  99. package/dist/components/ui/modal/upload/index.js +2 -0
  100. package/dist/components/ui/modal/upload/types.d.ts +79 -0
  101. package/dist/components/ui/modal/upload/types.js +1 -0
  102. package/dist/components/ui/modal/upload/ui/ModalUpload.svelte +268 -0
  103. package/dist/components/ui/modal/upload/ui/ModalUpload.svelte.d.ts +4 -0
  104. package/dist/components/ui/section/Section01.svelte +90 -0
  105. package/dist/components/ui/section/Section01.svelte.d.ts +4 -0
  106. package/dist/components/ui/section/Section02.svelte +68 -0
  107. package/dist/components/ui/section/Section02.svelte.d.ts +4 -0
  108. package/dist/components/ui/section/Section03.svelte +52 -0
  109. package/dist/components/ui/section/Section03.svelte.d.ts +4 -0
  110. package/dist/components/ui/section/index.d.ts +4 -0
  111. package/dist/components/ui/section/index.js +3 -0
  112. package/dist/components/ui/section/types.d.ts +46 -0
  113. package/dist/components/ui/section/types.js +1 -0
  114. package/dist/docs/DocsSidebar.svelte +58 -1
  115. package/dist/docs/components.d.ts +1 -1
  116. package/dist/docs/components.js +968 -0
  117. package/dist/globals.css +360 -0
  118. package/dist/index.d.ts +185 -0
  119. package/dist/index.js +185 -0
  120. package/dist/types/index.d.ts +53 -0
  121. package/package.json +1 -1
@@ -0,0 +1,171 @@
1
+ <script lang="ts">
2
+ import { cn } from "../../../utils";
3
+ import InputBase from "./ui/input-base.svelte";
4
+ import InputSelect from "./ui/input-select.svelte";
5
+ import InputRadio from "./ui/input-radio.svelte";
6
+ import InputCheckbox from "./ui/input-checkbox.svelte";
7
+ import InputTextarea from "./ui/input-textarea.svelte";
8
+ import InputToggle from "./ui/input-toggle.svelte";
9
+ import InputBadges from "./ui/input-badges.svelte";
10
+ import type { FormProps, FormInputConfig } from "./types";
11
+
12
+ let {
13
+ inputs = [],
14
+ onSubmit,
15
+ submitText = "Submit",
16
+ submitLoadingText = "Submitting...",
17
+ isLoading = false,
18
+ columns = 2,
19
+ gap = "gap-4",
20
+ class: className,
21
+ }: FormProps = $props();
22
+
23
+ let formValues = $state<Record<string, string | string[] | boolean>>(getInitialValues());
24
+
25
+ function getInitialValues(): Record<string, string | string[] | boolean> {
26
+ const values: Record<string, string | string[] | boolean> = {};
27
+ for (const input of inputs) {
28
+ if (input.type === "toggle") {
29
+ values[input.name] = (input.value as boolean) ?? false;
30
+ } else if (input.type === "checkbox" || input.type === "badges" || input.multiple) {
31
+ values[input.name] = (input.value as string[]) ?? [];
32
+ } else {
33
+ values[input.name] = (input.value as string) ?? "";
34
+ }
35
+ }
36
+ return values;
37
+ }
38
+
39
+ function handleTextChange(name: string, e: Event) {
40
+ const val = (e.target as HTMLInputElement | HTMLTextAreaElement).value;
41
+ formValues[name] = val;
42
+ const config = inputs.find((i) => i.name === name);
43
+ if (config?.onChange) {
44
+ (config.onChange as (v: string | string[] | boolean) => void)(val);
45
+ }
46
+ }
47
+
48
+ function handleSelectChange(name: string, val: string | string[]) {
49
+ formValues[name] = val;
50
+ const config = inputs.find((i) => i.name === name);
51
+ if (config?.onChange) {
52
+ (config.onChange as (v: string | string[] | boolean) => void)(val);
53
+ }
54
+ }
55
+
56
+ function handleToggleChange(name: string, val: boolean) {
57
+ formValues[name] = val;
58
+ const config = inputs.find((i) => i.name === name);
59
+ if (config?.onChange) {
60
+ (config.onChange as (v: string | string[] | boolean) => void)(val);
61
+ }
62
+ }
63
+
64
+ function handleSubmit(e: Event) {
65
+ e.preventDefault();
66
+ if (isLoading) return;
67
+ onSubmit?.(formValues);
68
+ }
69
+
70
+ function getColSpan(input: FormInputConfig): string {
71
+ if (input.fill) return "col-span-1 md:col-span-2";
72
+ return "col-span-1";
73
+ }
74
+ </script>
75
+
76
+ <form
77
+ onsubmit={handleSubmit}
78
+ class={cn(
79
+ "grid w-full",
80
+ columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1",
81
+ gap,
82
+ className
83
+ )}
84
+ >
85
+ {#each inputs as input (input.name)}
86
+ <div class={getColSpan(input)}>
87
+ {#if input.type === "select"}
88
+ <InputSelect
89
+ label={input.label}
90
+ isLabel
91
+ placeholder={input.placeholder}
92
+ options={input.options ?? []}
93
+ multiple={input.multiple ?? false}
94
+ disabled={input.disabled ?? false}
95
+ required={input.required ?? false}
96
+ bind:value={() => formValues[input.name] as string, (v) => handleSelectChange(input.name, v)}
97
+ />
98
+ {:else if input.type === "radio"}
99
+ <InputRadio
100
+ label={input.label}
101
+ isLabel
102
+ name={input.name}
103
+ options={input.options ?? []}
104
+ disabled={input.disabled ?? false}
105
+ required={input.required ?? false}
106
+ bind:value={() => formValues[input.name] as string, (v) => handleSelectChange(input.name, v)}
107
+ />
108
+ {:else if input.type === "checkbox"}
109
+ <InputCheckbox
110
+ label={input.label}
111
+ isLabel
112
+ name={input.name}
113
+ options={input.options ?? []}
114
+ disabled={input.disabled ?? false}
115
+ required={input.required ?? false}
116
+ bind:value={() => formValues[input.name] as string[], (v) => handleSelectChange(input.name, v)}
117
+ />
118
+ {:else if input.type === "textarea"}
119
+ <InputTextarea
120
+ label={input.label}
121
+ isLabel
122
+ placeholder={input.placeholder}
123
+ disabled={input.disabled ?? false}
124
+ required={input.required ?? false}
125
+ rows={input.rows ?? 4}
126
+ bind:value={() => formValues[input.name] as string, (v) => handleSelectChange(input.name, v)}
127
+ />
128
+ {:else if input.type === "toggle"}
129
+ <InputToggle
130
+ label={input.label}
131
+ isLabel
132
+ description={input.description}
133
+ disabled={input.disabled ?? false}
134
+ bind:value={() => formValues[input.name] as boolean, (v) => handleToggleChange(input.name, v)}
135
+ />
136
+ {:else if input.type === "badges"}
137
+ <InputBadges
138
+ label={input.label}
139
+ isLabel
140
+ placeholder={input.placeholder}
141
+ suggestions={input.suggestions ?? []}
142
+ maxTags={input.maxTags ?? 0}
143
+ disabled={input.disabled ?? false}
144
+ required={input.required ?? false}
145
+ bind:value={() => formValues[input.name] as string[], (v) => handleSelectChange(input.name, v)}
146
+ />
147
+ {:else}
148
+ <InputBase
149
+ type={input.type === "phone" ? "tel" : input.type}
150
+ label={input.label}
151
+ isLabel
152
+ isIcon
153
+ placeholder={input.placeholder}
154
+ disabled={input.disabled ?? false}
155
+ required={input.required ?? false}
156
+ bind:value={() => formValues[input.name] as string, (v) => handleSelectChange(input.name, v)}
157
+ />
158
+ {/if}
159
+ </div>
160
+ {/each}
161
+
162
+ <div class={cn("flex justify-end", columns === 2 ? "col-span-1 md:col-span-2" : "col-span-1")}>
163
+ <button
164
+ type="submit"
165
+ disabled={isLoading}
166
+ class="inline-flex items-center gap-2 rounded-lg bg-primary px-6 py-2.5 text-sm font-semibold text-primary-foreground transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-50"
167
+ >
168
+ {isLoading ? submitLoadingText : submitText}
169
+ </button>
170
+ </div>
171
+ </form>
@@ -0,0 +1,4 @@
1
+ import type { FormProps } from "./types";
2
+ declare const Form: import("svelte").Component<FormProps, {}, "">;
3
+ type Form = ReturnType<typeof Form>;
4
+ export default Form;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Opção para inputs do tipo select, radio ou checkbox.
3
+ * @property value - Valor identificador da opção.
4
+ * @property label - Texto visível da opção.
5
+ */
6
+ export type FormOption = {
7
+ value: string;
8
+ label: string;
9
+ };
10
+ /**
11
+ * Configuração de um input dentro do Form.
12
+ * Cada item define um campo do formulário com o seu tipo e propriedades.
13
+ *
14
+ * Tipos suportados:
15
+ * - `'text'` | `'email'` | `'password'` | `'date'` | `'phone'` — inputs de texto
16
+ * - `'select'` — dropdown/select com opções
17
+ * - `'radio'` — grupo de radio buttons
18
+ * - `'checkbox'` — grupo de checkboxes
19
+ * - `'textarea'` — área de texto
20
+ * - `'toggle'` — interruptor on/off
21
+ * - `'badges'` — input de tags/badges com sugestões
22
+ *
23
+ * @property type - Tipo do input.
24
+ * @property name - Nome do campo (usado como chave no objeto de dados).
25
+ * @property value - Valor inicial do campo.
26
+ * @property label - Label/rótulo do campo.
27
+ * @property placeholder - Texto placeholder.
28
+ * @property onChange - Callback ao alterar o valor.
29
+ * @property disabled - Desativa o campo.
30
+ * @property required - Marca como obrigatório.
31
+ * @property fill - Ocupa 2 colunas (true) ou 1 coluna (false/default).
32
+ * @property options - Opções para select, radio ou checkbox.
33
+ * @property multiple - Permite seleção múltipla (apenas select).
34
+ * @property rows - Número de linhas (apenas textarea).
35
+ * @property description - Texto descritivo (apenas toggle).
36
+ * @property suggestions - Sugestões de tags (apenas badges).
37
+ * @property maxTags - Número máximo de tags (apenas badges, 0 = ilimitado).
38
+ */
39
+ export type FormInputConfig = {
40
+ type: "text" | "email" | "password" | "date" | "phone" | "select" | "radio" | "checkbox" | "textarea" | "toggle" | "badges";
41
+ name: string;
42
+ value?: string | string[] | boolean;
43
+ label?: string;
44
+ placeholder?: string;
45
+ onChange?: (value: string | string[] | boolean) => void;
46
+ disabled?: boolean;
47
+ required?: boolean;
48
+ fill?: boolean;
49
+ options?: FormOption[];
50
+ multiple?: boolean;
51
+ rows?: number;
52
+ description?: string;
53
+ suggestions?: string[];
54
+ maxTags?: number;
55
+ };
56
+ /**
57
+ * Props do componente Form — formulário com grid responsivo de 2 colunas.
58
+ * Organiza os inputs numa grid, onde cada campo pode ocupar 1 ou 2 colunas via `fill`.
59
+ *
60
+ * @property inputs - Array de configurações dos inputs do formulário.
61
+ * @property onSubmit - Callback ao submeter o formulário (objeto com name:value).
62
+ * @property submitText - Texto do botão de submissão.
63
+ * @property submitLoadingText - Texto durante carregamento.
64
+ * @property isLoading - Estado de carregamento.
65
+ * @property columns - Número de colunas da grid (padrão: 2).
66
+ * @property gap - Espaçamento entre os campos.
67
+ * @property class - Classe CSS extra no container.
68
+ */
69
+ export type FormProps = {
70
+ inputs: FormInputConfig[];
71
+ onSubmit?: (data: Record<string, string | string[] | boolean>) => void;
72
+ submitText?: string;
73
+ submitLoadingText?: string;
74
+ isLoading?: boolean;
75
+ columns?: 1 | 2;
76
+ gap?: string;
77
+ class?: string;
78
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,214 @@
1
+ <script lang="ts">
2
+ import { Label } from "../../label";
3
+ import { cn } from "../../../../utils";
4
+ import { Cancel01Icon } from "@hugeicons/core-free-icons";
5
+ import { HugeiconsIcon } from "@hugeicons/svelte";
6
+
7
+ let {
8
+ label,
9
+ isLabel = false,
10
+ value = $bindable([]),
11
+ disabled = false,
12
+ required = false,
13
+ placeholder = "Add a tag...",
14
+ suggestions = [],
15
+ maxTags = 0,
16
+ class: className,
17
+ }: {
18
+ label?: string;
19
+ isLabel?: boolean;
20
+ value?: string[];
21
+ disabled?: boolean;
22
+ required?: boolean;
23
+ placeholder?: string;
24
+ suggestions?: string[];
25
+ maxTags?: number;
26
+ class?: string;
27
+ } = $props();
28
+
29
+ let inputValue = $state("");
30
+ let isInputFocused = $state(false);
31
+ let showSuggestions = $state(false);
32
+
33
+ const filteredSuggestions = $derived(
34
+ suggestions.filter(
35
+ (s) =>
36
+ !value.includes(s) &&
37
+ s.toLowerCase().includes(inputValue.toLowerCase())
38
+ )
39
+ );
40
+
41
+ function addTag(tag: string) {
42
+ const trimmed = tag.trim();
43
+ if (!trimmed || disabled) return;
44
+ if (value.includes(trimmed)) return;
45
+ if (maxTags > 0 && value.length >= maxTags) return;
46
+ value = [...value, trimmed];
47
+ inputValue = "";
48
+ showSuggestions = false;
49
+ }
50
+
51
+ function removeTag(tag: string) {
52
+ if (disabled) return;
53
+ value = value.filter((t) => t !== tag);
54
+ }
55
+
56
+ function handleKeydown(e: KeyboardEvent) {
57
+ if (e.key === "Enter") {
58
+ e.preventDefault();
59
+ addTag(inputValue);
60
+ } else if (e.key === "Backspace" && !inputValue && value.length > 0) {
61
+ value = value.slice(0, -1);
62
+ }
63
+ }
64
+
65
+ function handleInput(e: Event) {
66
+ inputValue = (e.target as HTMLInputElement).value;
67
+ showSuggestions = inputValue.length > 0 && filteredSuggestions.length > 0;
68
+ }
69
+
70
+ function handleFocus() {
71
+ isInputFocused = true;
72
+ if (inputValue.length > 0 && filteredSuggestions.length > 0) {
73
+ showSuggestions = true;
74
+ }
75
+ }
76
+
77
+ function handleBlur() {
78
+ isInputFocused = false;
79
+ setTimeout(() => (showSuggestions = false), 150);
80
+ }
81
+
82
+ function selectSuggestion(suggestion: string) {
83
+ addTag(suggestion);
84
+ inputValue = "";
85
+ }
86
+ </script>
87
+
88
+ {#if isLabel}
89
+ <div class={cn("flex w-full flex-col gap-2", className)}>
90
+ {#if label}
91
+ <Label>{label}</Label>
92
+ {/if}
93
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
94
+ <div
95
+ class={cn(
96
+ "flex min-h-[42px] flex-wrap items-center gap-1.5 rounded-md border border-input bg-card px-3 py-1.5 transition-colors",
97
+ isInputFocused && "ring-2 ring-ring ring-offset-2 ring-offset-background",
98
+ disabled && "cursor-not-allowed opacity-50"
99
+ )}
100
+ onclick={() => !disabled && document.getElementById(`badges-input-${label}`)?.focus()}
101
+ >
102
+ {#each value as tag (tag)}
103
+ <span class="inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2.5 py-0.5 text-xs font-medium text-foreground">
104
+ {tag}
105
+ <button
106
+ type="button"
107
+ {disabled}
108
+ class="ml-0.5 rounded-full p-0.5 transition-colors hover:bg-destructive/10 hover:text-destructive"
109
+ onclick={(e) => { e.stopPropagation(); removeTag(tag); }}
110
+ >
111
+ <HugeiconsIcon icon={Cancel01Icon} class="size-3" />
112
+ </button>
113
+ </span>
114
+ {/each}
115
+
116
+ {#if maxTags === 0 || value.length < maxTags}
117
+ <input
118
+ id="badges-input-{label}"
119
+ type="text"
120
+ bind:value={inputValue}
121
+ {placeholder}
122
+ {disabled}
123
+ {required}
124
+ class="min-w-[120px] flex-1 bg-transparent py-1 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed"
125
+ onkeydown={handleKeydown}
126
+ oninput={handleInput}
127
+ onfocus={handleFocus}
128
+ onblur={handleBlur}
129
+ />
130
+ {/if}
131
+ </div>
132
+
133
+ {#if showSuggestions && filteredSuggestions.length > 0}
134
+ <div class="max-h-40 overflow-auto rounded-md border border-border bg-card shadow-md">
135
+ {#each filteredSuggestions as suggestion (suggestion)}
136
+ <button
137
+ type="button"
138
+ class="flex w-full items-center px-3 py-2 text-sm transition-colors hover:bg-muted"
139
+ onmousedown={(e) => { e.preventDefault(); selectSuggestion(suggestion); }}
140
+ >
141
+ {suggestion}
142
+ </button>
143
+ {/each}
144
+ </div>
145
+ {/if}
146
+
147
+ {#if maxTags > 0}
148
+ <p class="text-xs text-muted-foreground">
149
+ {value.length}/{maxTags} tags
150
+ </p>
151
+ {/if}
152
+ </div>
153
+ {:else}
154
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
155
+ <div
156
+ class={cn(
157
+ "flex min-h-[42px] flex-wrap items-center gap-1.5 rounded-md border border-input bg-card px-3 py-1.5 transition-colors",
158
+ isInputFocused && "ring-2 ring-ring ring-offset-2 ring-offset-background",
159
+ disabled && "cursor-not-allowed opacity-50",
160
+ className
161
+ )}
162
+ onclick={() => !disabled && document.getElementById(`badges-input-${label}`)?.focus()}
163
+ >
164
+ {#each value as tag (tag)}
165
+ <span class="inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2.5 py-0.5 text-xs font-medium text-foreground">
166
+ {tag}
167
+ <button
168
+ type="button"
169
+ {disabled}
170
+ class="ml-0.5 rounded-full p-0.5 transition-colors hover:bg-destructive/10 hover:text-destructive"
171
+ onclick={(e) => { e.stopPropagation(); removeTag(tag); }}
172
+ >
173
+ <HugeiconsIcon icon={Cancel01Icon} class="size-3" />
174
+ </button>
175
+ </span>
176
+ {/each}
177
+
178
+ {#if maxTags === 0 || value.length < maxTags}
179
+ <input
180
+ id="badges-input-{label}"
181
+ type="text"
182
+ bind:value={inputValue}
183
+ {placeholder}
184
+ {disabled}
185
+ {required}
186
+ class="min-w-[120px] flex-1 bg-transparent py-1 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed"
187
+ onkeydown={handleKeydown}
188
+ oninput={handleInput}
189
+ onfocus={handleFocus}
190
+ onblur={handleBlur}
191
+ />
192
+ {/if}
193
+ </div>
194
+
195
+ {#if showSuggestions && filteredSuggestions.length > 0}
196
+ <div class="max-h-40 overflow-auto rounded-md border border-border bg-card shadow-md">
197
+ {#each filteredSuggestions as suggestion (suggestion)}
198
+ <button
199
+ type="button"
200
+ class="flex w-full items-center px-3 py-2 text-sm transition-colors hover:bg-muted"
201
+ onmousedown={(e) => { e.preventDefault(); selectSuggestion(suggestion); }}
202
+ >
203
+ {suggestion}
204
+ </button>
205
+ {/each}
206
+ </div>
207
+ {/if}
208
+
209
+ {#if maxTags > 0}
210
+ <p class="text-xs text-muted-foreground">
211
+ {value.length}/{maxTags} tags
212
+ </p>
213
+ {/if}
214
+ {/if}
@@ -0,0 +1,14 @@
1
+ type $$ComponentProps = {
2
+ label?: string;
3
+ isLabel?: boolean;
4
+ value?: string[];
5
+ disabled?: boolean;
6
+ required?: boolean;
7
+ placeholder?: string;
8
+ suggestions?: string[];
9
+ maxTags?: number;
10
+ class?: string;
11
+ };
12
+ declare const InputBadges: import("svelte").Component<$$ComponentProps, {}, "value">;
13
+ type InputBadges = ReturnType<typeof InputBadges>;
14
+ export default InputBadges;
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import { Label } from "../../label";
3
+ import { Checkbox } from "../../checkbox";
4
+ import { cn } from "../../../../utils";
5
+
6
+ type CheckboxOption = { value: string; label: string };
7
+
8
+ let {
9
+ label,
10
+ isLabel = false,
11
+ value = $bindable([]),
12
+ disabled = false,
13
+ required = false,
14
+ options = [],
15
+ name = "",
16
+ class: className,
17
+ }: {
18
+ label?: string;
19
+ isLabel?: boolean;
20
+ value?: string[];
21
+ disabled?: boolean;
22
+ required?: boolean;
23
+ options?: CheckboxOption[];
24
+ name?: string;
25
+ class?: string;
26
+ } = $props();
27
+
28
+ function handleToggle(optionValue: string) {
29
+ if (disabled) return;
30
+ const idx = value.indexOf(optionValue);
31
+ if (idx >= 0) {
32
+ value = value.filter((v) => v !== optionValue);
33
+ } else {
34
+ value = [...value, optionValue];
35
+ }
36
+ }
37
+
38
+ function isChecked(optionValue: string): boolean {
39
+ return value.includes(optionValue);
40
+ }
41
+ </script>
42
+
43
+ {#if isLabel}
44
+ <div class={cn("flex w-full flex-col gap-2", className)}>
45
+ {#if label}
46
+ <Label>{label}</Label>
47
+ {/if}
48
+ <div class="flex flex-wrap gap-4">
49
+ {#each options as option (option.value)}
50
+ <label
51
+ class={cn(
52
+ "flex cursor-pointer items-center gap-2 text-sm",
53
+ disabled && "cursor-not-allowed opacity-50"
54
+ )}
55
+ >
56
+ <Checkbox
57
+ {disabled}
58
+ {name}
59
+ checked={isChecked(option.value)}
60
+ onCheckedChange={() => handleToggle(option.value)}
61
+ />
62
+ <span class="text-foreground">{option.label}</span>
63
+ </label>
64
+ {/each}
65
+ </div>
66
+ </div>
67
+ {:else}
68
+ <div class={cn("flex flex-wrap gap-4", className)}>
69
+ {#each options as option (option.value)}
70
+ <label
71
+ class={cn(
72
+ "flex cursor-pointer items-center gap-2 text-sm",
73
+ disabled && "cursor-not-allowed opacity-50"
74
+ )}
75
+ >
76
+ <Checkbox
77
+ {disabled}
78
+ {name}
79
+ checked={isChecked(option.value)}
80
+ onCheckedChange={() => handleToggle(option.value)}
81
+ />
82
+ <span class="text-foreground">{option.label}</span>
83
+ </label>
84
+ {/each}
85
+ </div>
86
+ {/if}
@@ -0,0 +1,17 @@
1
+ type CheckboxOption = {
2
+ value: string;
3
+ label: string;
4
+ };
5
+ type $$ComponentProps = {
6
+ label?: string;
7
+ isLabel?: boolean;
8
+ value?: string[];
9
+ disabled?: boolean;
10
+ required?: boolean;
11
+ options?: CheckboxOption[];
12
+ name?: string;
13
+ class?: string;
14
+ };
15
+ declare const InputCheckbox: import("svelte").Component<$$ComponentProps, {}, "value">;
16
+ type InputCheckbox = ReturnType<typeof InputCheckbox>;
17
+ export default InputCheckbox;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import { Label } from "../../label";
3
+ import { cn } from "../../../../utils";
4
+
5
+ type RadioOption = { value: string; label: string };
6
+
7
+ let {
8
+ label,
9
+ isLabel = false,
10
+ value = $bindable(""),
11
+ disabled = false,
12
+ required = false,
13
+ options = [],
14
+ name = "",
15
+ class: className,
16
+ }: {
17
+ label?: string;
18
+ isLabel?: boolean;
19
+ value?: string;
20
+ disabled?: boolean;
21
+ required?: boolean;
22
+ options?: RadioOption[];
23
+ name?: string;
24
+ class?: string;
25
+ } = $props();
26
+
27
+ function handleSelect(option: RadioOption) {
28
+ if (disabled) return;
29
+ value = option.value;
30
+ }
31
+ </script>
32
+
33
+ {#if isLabel}
34
+ <div class={cn("flex w-full flex-col gap-2", className)}>
35
+ {#if label}
36
+ <Label>{label}</Label>
37
+ {/if}
38
+ <div class="flex flex-wrap gap-4">
39
+ {#each options as option (option.value)}
40
+ <label
41
+ class={cn(
42
+ "flex cursor-pointer items-center gap-2 text-sm",
43
+ disabled && "cursor-not-allowed opacity-50"
44
+ )}
45
+ >
46
+ <input
47
+ type="radio"
48
+ {name}
49
+ {disabled}
50
+ {required}
51
+ checked={value === option.value}
52
+ onchange={() => handleSelect(option)}
53
+ class="size-4 border-input text-primary accent-primary"
54
+ />
55
+ <span class="text-foreground">{option.label}</span>
56
+ </label>
57
+ {/each}
58
+ </div>
59
+ </div>
60
+ {:else}
61
+ <div class={cn("flex flex-wrap gap-4", className)}>
62
+ {#each options as option (option.value)}
63
+ <label
64
+ class={cn(
65
+ "flex cursor-pointer items-center gap-2 text-sm",
66
+ disabled && "cursor-not-allowed opacity-50"
67
+ )}
68
+ >
69
+ <input
70
+ type="radio"
71
+ {name}
72
+ {disabled}
73
+ {required}
74
+ checked={value === option.value}
75
+ onchange={() => handleSelect(option)}
76
+ class="size-4 border-input text-primary accent-primary"
77
+ />
78
+ <span class="text-foreground">{option.label}</span>
79
+ </label>
80
+ {/each}
81
+ </div>
82
+ {/if}
@@ -0,0 +1,17 @@
1
+ type RadioOption = {
2
+ value: string;
3
+ label: string;
4
+ };
5
+ type $$ComponentProps = {
6
+ label?: string;
7
+ isLabel?: boolean;
8
+ value?: string;
9
+ disabled?: boolean;
10
+ required?: boolean;
11
+ options?: RadioOption[];
12
+ name?: string;
13
+ class?: string;
14
+ };
15
+ declare const InputRadio: import("svelte").Component<$$ComponentProps, {}, "value">;
16
+ type InputRadio = ReturnType<typeof InputRadio>;
17
+ export default InputRadio;