grav-svelte 0.0.98 → 0.0.99

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.
@@ -0,0 +1,171 @@
1
+ <script lang="ts">
2
+ import "../typography.css";
3
+
4
+ export let valueVar: string = "";
5
+ export let label: string;
6
+ export let disabled = false;
7
+ export let obligatory = false;
8
+ export let icon: string | null = null;
9
+ export let validation: boolean = false;
10
+
11
+ let validationMessage = "";
12
+ let isValid = true;
13
+
14
+ $: {
15
+ if (valueVar) {
16
+ // Email validation using standard regex
17
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
18
+
19
+ if (!emailRegex.test(valueVar)) {
20
+ validationMessage = "Invalid email format";
21
+ isValid = false;
22
+ } else {
23
+ validationMessage = "Email is valid";
24
+ isValid = true;
25
+ }
26
+ } else {
27
+ if (obligatory) {
28
+ validationMessage = "Email is required";
29
+ isValid = false;
30
+ } else {
31
+ validationMessage = "";
32
+ isValid = true;
33
+ }
34
+ }
35
+ }
36
+ </script>
37
+
38
+ <div class="input-container">
39
+ {#if icon}
40
+ <div class="icon-wrapper">
41
+ <i class="{icon} icon"></i>
42
+ </div>
43
+ {/if}
44
+ <div class="input-wrapper">
45
+ <input
46
+ {disabled}
47
+ type="email"
48
+ bind:value={valueVar}
49
+ placeholder=" "
50
+ class="input-field"
51
+ />
52
+
53
+ <label for={valueVar} class="input-label"
54
+ >{label}
55
+ {#if obligatory}
56
+ <span class="required-mark"> *</span>
57
+ {/if}</label
58
+ >
59
+ </div>
60
+ </div>
61
+ {#if validation && validationMessage}
62
+ <div
63
+ class="validation-message"
64
+ class:valid={isValid}
65
+ class:invalid={!isValid}
66
+ >
67
+ {validationMessage}
68
+ </div>
69
+ {/if}
70
+
71
+ <style>
72
+ .input-container {
73
+ display: flex;
74
+ align-items: center;
75
+ border: var(--grav-crud-input-border-width) solid
76
+ var(--grav-crud-color-neutral);
77
+ border-radius: 0.5rem;
78
+ padding-left: 0.5rem;
79
+ padding-right: 0.5rem;
80
+ padding-top: 0.2rem;
81
+ padding-bottom: 0.2rem;
82
+ margin-top: 1.95rem;
83
+ height: fit-content;
84
+ }
85
+
86
+ .icon-wrapper {
87
+ width: 1rem;
88
+ position: relative;
89
+ margin-right: 0.5rem;
90
+ }
91
+
92
+ .icon {
93
+ position: absolute;
94
+ top: -0.4rem;
95
+ left: 0.25rem;
96
+ color: var(--grav-crud-color-neutral);
97
+ }
98
+
99
+ .input-wrapper {
100
+ position: relative;
101
+ z-index: 0;
102
+ width: 100%;
103
+ }
104
+
105
+ .input-field {
106
+ display: block;
107
+ padding: 0.3rem;
108
+ width: 100%;
109
+ font-size: 1rem;
110
+ color: var(--grav-crud-color-neutral);
111
+ background: transparent;
112
+ appearance: none;
113
+ }
114
+
115
+ .input-field:focus {
116
+ outline: none;
117
+ }
118
+
119
+ .input-label {
120
+ position: absolute;
121
+ font-size: 1rem;
122
+ text-align: left;
123
+ color: var(--grav-crud-color-neutral);
124
+ transition: all 0.3s;
125
+ top: 0.25rem;
126
+ left: 0.25rem;
127
+ z-index: -10;
128
+ transform-origin: left;
129
+ }
130
+
131
+ .input-field:focus + .input-label,
132
+ .input-field:not(:placeholder-shown) + .input-label {
133
+ left: 0;
134
+ top: 0;
135
+ color: var(--grav-crud-color-neutral);
136
+ translate: -0.6rem -2.05rem;
137
+ scale: 1;
138
+ }
139
+
140
+ .input-field:placeholder-shown + .input-label {
141
+ transform: translateY(0) scale(1);
142
+ }
143
+
144
+ .required-mark {
145
+ color: #dc2626;
146
+ }
147
+
148
+ .no-margin {
149
+ margin-top: 0;
150
+ }
151
+
152
+ .no-margin .input-field:focus + .input-label,
153
+ .no-margin .input-field:not(:placeholder-shown) + .input-label {
154
+ translate: -0.6rem -1.4rem;
155
+ font-size: 0.7rem;
156
+ }
157
+
158
+ .validation-message {
159
+ font-size: 0.875rem;
160
+ margin-top: 0.25rem;
161
+ color: var(--grav-crud-color-neutral);
162
+ }
163
+
164
+ .validation-message.valid {
165
+ color: #10b981;
166
+ }
167
+
168
+ .validation-message.invalid {
169
+ color: #dc2626;
170
+ }
171
+ </style>
@@ -0,0 +1,22 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import "../typography.css";
3
+ declare const __propDef: {
4
+ props: {
5
+ valueVar?: string;
6
+ label: string;
7
+ disabled?: boolean;
8
+ obligatory?: boolean;
9
+ icon?: string | null;
10
+ validation?: boolean;
11
+ };
12
+ events: {
13
+ [evt: string]: CustomEvent<any>;
14
+ };
15
+ slots: {};
16
+ };
17
+ export type InputFormMailProps = typeof __propDef.props;
18
+ export type InputFormMailEvents = typeof __propDef.events;
19
+ export type InputFormMailSlots = typeof __propDef.slots;
20
+ export default class InputFormMail extends SvelteComponentTyped<InputFormMailProps, InputFormMailEvents, InputFormMailSlots> {
21
+ }
22
+ export {};
@@ -0,0 +1,229 @@
1
+ <script lang="ts">
2
+ import "../typography.css";
3
+
4
+ interface CountryOption {
5
+ value: string;
6
+ label: string;
7
+ }
8
+
9
+ export let valueVar: string = "";
10
+ export let label: string;
11
+ export let disabled = false;
12
+ export let obligatory = false;
13
+ export let defaultDialCode: string = "+52";
14
+ export let validation: boolean = false;
15
+
16
+ // Internal state
17
+ let selectedDialCode: string = defaultDialCode;
18
+ let phoneNumber: string = "";
19
+ let validationMessage = "";
20
+ let isValid = true;
21
+
22
+ // Country list with flag emojis (only flag + dial code)
23
+ const countries: CountryOption[] = [
24
+ { value: "+1", label: "🇺🇸 +1" },
25
+ { value: "+1", label: "🇨🇦 +1" },
26
+ { value: "+52", label: "🇲🇽 +52" },
27
+ { value: "+34", label: "🇪🇸 +34" },
28
+ { value: "+54", label: "🇦🇷 +54" },
29
+ { value: "+56", label: "🇨🇱 +56" },
30
+ { value: "+57", label: "🇨🇴 +57" },
31
+ { value: "+51", label: "🇵🇪 +51" },
32
+ { value: "+58", label: "🇻🇪 +58" },
33
+ { value: "+593", label: "🇪🇨 +593" },
34
+ { value: "+55", label: "🇧🇷 +55" },
35
+ { value: "+598", label: "🇺🇾 +598" },
36
+ { value: "+595", label: "🇵🇾 +595" },
37
+ { value: "+591", label: "🇧🇴 +591" },
38
+ { value: "+506", label: "🇨🇷 +506" },
39
+ { value: "+502", label: "🇬🇹 +502" },
40
+ { value: "+504", label: "🇭🇳 +504" },
41
+ { value: "+505", label: "🇳🇮 +505" },
42
+ { value: "+507", label: "🇵🇦 +507" },
43
+ { value: "+503", label: "🇸🇻 +503" },
44
+ { value: "+1", label: "🇩🇴 +1" },
45
+ { value: "+1", label: "🇵🇷 +1" },
46
+ { value: "+53", label: "🇨🇺 +53" },
47
+ { value: "+44", label: "🇬🇧 +44" },
48
+ { value: "+33", label: "🇫🇷 +33" },
49
+ { value: "+49", label: "🇩🇪 +49" },
50
+ { value: "+39", label: "🇮🇹 +39" },
51
+ { value: "+351", label: "🇵🇹 +351" },
52
+ { value: "+31", label: "🇳🇱 +31" },
53
+ { value: "+32", label: "🇧🇪 +32" },
54
+ ];
55
+
56
+ // Concatenate dial code and phone number
57
+ $: {
58
+ if (selectedDialCode && phoneNumber) {
59
+ valueVar = selectedDialCode + phoneNumber.replace(/\D/g, "");
60
+ } else if (selectedDialCode) {
61
+ valueVar = selectedDialCode;
62
+ } else {
63
+ valueVar = "";
64
+ }
65
+
66
+ // Validation logic
67
+ if (validation) {
68
+ const cleanNumber = phoneNumber.replace(/\D/g, "");
69
+
70
+ if (phoneNumber && !/^\d+$/.test(cleanNumber)) {
71
+ validationMessage = "Phone number must contain only digits";
72
+ isValid = false;
73
+ } else if (cleanNumber && cleanNumber.length < 7) {
74
+ validationMessage = "Phone number must be at least 7 digits";
75
+ isValid = false;
76
+ } else if (cleanNumber && cleanNumber.length > 15) {
77
+ validationMessage = "Phone number must not exceed 15 digits";
78
+ isValid = false;
79
+ } else if (cleanNumber) {
80
+ validationMessage = "Phone number is valid";
81
+ isValid = true;
82
+ } else {
83
+ validationMessage = "";
84
+ isValid = true;
85
+ }
86
+ }
87
+ }
88
+
89
+ // Clean phone number input (only allow digits)
90
+ function handlePhoneInput(event: Event) {
91
+ const input = event.target as HTMLInputElement;
92
+ const cleaned = input.value.replace(/\D/g, "");
93
+ phoneNumber = cleaned;
94
+ }
95
+ </script>
96
+
97
+ <div class="phone-container">
98
+ <label class="phone-label">
99
+ {label}
100
+ {#if obligatory}
101
+ <span class="required-mark"> *</span>
102
+ {/if}
103
+ </label>
104
+
105
+ <div class="phone-input-wrapper">
106
+ <div class="country-select-wrapper">
107
+ <select
108
+ bind:value={selectedDialCode}
109
+ {disabled}
110
+ class="country-select"
111
+ >
112
+ {#each countries as country}
113
+ <option value={country.value}>{country.label}</option>
114
+ {/each}
115
+ </select>
116
+ </div>
117
+
118
+ <div class="phone-number-wrapper">
119
+ <input
120
+ type="tel"
121
+ bind:value={phoneNumber}
122
+ on:input={handlePhoneInput}
123
+ {disabled}
124
+ placeholder="Phone number"
125
+ class="phone-input"
126
+ />
127
+ </div>
128
+ </div>
129
+
130
+ {#if validation && validationMessage}
131
+ <div
132
+ class="validation-message"
133
+ class:valid={isValid}
134
+ class:invalid={!isValid}
135
+ >
136
+ {validationMessage}
137
+ </div>
138
+ {/if}
139
+ </div>
140
+
141
+ <style>
142
+ .phone-container {
143
+ display: flex;
144
+ flex-direction: column;
145
+ width: 100%;
146
+ margin-top: 1.95rem;
147
+ }
148
+
149
+ .phone-label {
150
+ font-size: 1rem;
151
+ color: var(--grav-crud-color-neutral);
152
+ margin-bottom: 0.25rem;
153
+ }
154
+
155
+ .required-mark {
156
+ color: #dc2626;
157
+ }
158
+
159
+ .phone-input-wrapper {
160
+ display: grid;
161
+ grid-template-columns: 80px 1fr;
162
+ border: var(--grav-crud-input-border-width) solid
163
+ var(--grav-crud-color-neutral);
164
+ border-radius: 0.5rem;
165
+ overflow: hidden;
166
+ }
167
+
168
+ .country-select-wrapper {
169
+ border-right: var(--grav-crud-input-border-width) solid
170
+ var(--grav-crud-color-neutral);
171
+ background-color: transparent;
172
+ }
173
+
174
+ .country-select {
175
+ width: 100%;
176
+ height: 100%;
177
+ padding: 0.5rem;
178
+ padding-right: 0.25rem;
179
+ font-size: 1rem;
180
+ color: var(--grav-crud-color-neutral);
181
+ background-color: transparent;
182
+ border: none;
183
+ outline: none;
184
+ cursor: pointer;
185
+ appearance: none;
186
+ -webkit-appearance: none;
187
+ -moz-appearance: none;
188
+ }
189
+
190
+ .country-select:disabled {
191
+ cursor: not-allowed;
192
+ opacity: 0.6;
193
+ }
194
+
195
+ .phone-number-wrapper {
196
+ display: flex;
197
+ align-items: center;
198
+ padding: 0 0.5rem;
199
+ }
200
+
201
+ .phone-input {
202
+ width: 100%;
203
+ padding: 0.5rem;
204
+ font-size: 1rem;
205
+ color: var(--grav-crud-color-neutral);
206
+ background: transparent;
207
+ border: none;
208
+ outline: none;
209
+ }
210
+
211
+ .phone-input::placeholder {
212
+ color: var(--grav-crud-color-neutral);
213
+ opacity: 0.5;
214
+ }
215
+
216
+ .validation-message {
217
+ font-size: 0.875rem;
218
+ margin-top: 0.25rem;
219
+ color: var(--grav-crud-color-neutral);
220
+ }
221
+
222
+ .validation-message.valid {
223
+ color: #10b981;
224
+ }
225
+
226
+ .validation-message.invalid {
227
+ color: #dc2626;
228
+ }
229
+ </style>
@@ -0,0 +1,22 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import "../typography.css";
3
+ declare const __propDef: {
4
+ props: {
5
+ valueVar?: string;
6
+ label: string;
7
+ disabled?: boolean;
8
+ obligatory?: boolean;
9
+ defaultDialCode?: string;
10
+ validation?: boolean;
11
+ };
12
+ events: {
13
+ [evt: string]: CustomEvent<any>;
14
+ };
15
+ slots: {};
16
+ };
17
+ export type InputFormPhoneProps = typeof __propDef.props;
18
+ export type InputFormPhoneEvents = typeof __propDef.events;
19
+ export type InputFormPhoneSlots = typeof __propDef.slots;
20
+ export default class InputFormPhone extends SvelteComponentTyped<InputFormPhoneProps, InputFormPhoneEvents, InputFormPhoneSlots> {
21
+ }
22
+ export {};
@@ -9,4 +9,6 @@ export { default as InputFormNumber } from './InputFormNumber.svelte';
9
9
  export { default as InputFormSelect } from './InputFormSelect.svelte';
10
10
  export { default as InputFormText } from './InputFormText.svelte';
11
11
  export { default as InputFormPassword } from './InputFormPassword.svelte';
12
+ export { default as InputFormMail } from './InputFormMail.svelte';
13
+ export { default as InputFormPhone } from './InputFormPhone.svelte';
12
14
  export { default as InputFormTextArea } from './InputFormTextArea.svelte';
@@ -9,4 +9,6 @@ export { default as InputFormNumber } from './InputFormNumber.svelte';
9
9
  export { default as InputFormSelect } from './InputFormSelect.svelte';
10
10
  export { default as InputFormText } from './InputFormText.svelte';
11
11
  export { default as InputFormPassword } from './InputFormPassword.svelte';
12
+ export { default as InputFormMail } from './InputFormMail.svelte';
13
+ export { default as InputFormPhone } from './InputFormPhone.svelte';
12
14
  export { default as InputFormTextArea } from './InputFormTextArea.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grav-svelte",
3
- "version": "0.0.98",
3
+ "version": "0.0.99",
4
4
  "description": "A collection of Svelte components",
5
5
  "license": "MIT",
6
6
  "scripts": {