fancy-ui-ts 1.0.0 → 1.1.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
@@ -3,8 +3,234 @@
3
3
  A lightweight, cool, customizable Web Component library built with TypeScript.
4
4
  Works with **React**, **Vue**, **Angular**, **Svelte**, or plain HTML/JS.
5
5
 
6
- ## 📦 Installation
6
+ ## Installation
7
7
 
8
8
  ```bash
9
9
  npm install fancy-ui-ts
10
- ```
10
+ ```
11
+
12
+ -----
13
+
14
+ ## 1\. Quick Start
15
+
16
+ ### Registration
17
+
18
+ Before using a component, you must register it with the browser.
19
+
20
+ ```typescript
21
+ import { defineInput } from './index';
22
+
23
+ defineInput();
24
+ ```
25
+
26
+ You can also register every component at once:
27
+
28
+ ```typescript
29
+ import { defineAll } from './index';
30
+
31
+ defineAll();
32
+ ```
33
+
34
+ ### Basic HTML Usage
35
+
36
+ ```html
37
+ <form>
38
+ <fc-input
39
+ name="username"
40
+ placeholder="Enter username"
41
+ required
42
+ minlength="3"
43
+ >
44
+ </fc-input>
45
+
46
+ <fc-input
47
+ type="password"
48
+ name="password"
49
+ required
50
+ >
51
+ </fc-input>
52
+ </form>
53
+ ```
54
+
55
+ -----
56
+
57
+ ## 2\. Setting Properties & Attributes
58
+
59
+ Most fc-components have properties that can be set as custom attributes, they also accepts default attributes. You can configure the component using HTML Attributes or JavaScript Properties.
60
+
61
+ ### Via HTML Attributes
62
+
63
+ Use this for static configuration or simple strings.
64
+
65
+ ```html
66
+ <fc-input
67
+ label="Email"
68
+ type="email"
69
+ disabled
70
+ placeholder="john@doe.com"
71
+ ></fc-input>
72
+ ```
73
+
74
+ ### Via JavaScript Properties
75
+
76
+ In some cases, you'll need to pass dynamic data, complex objects, arrays or event listeners. You can't do that as attributes, you must
77
+ pass them with Javascript:
78
+
79
+ ```javascript
80
+ const input = document.querySelector('fc-input');
81
+
82
+ input.value = "New Value";
83
+ input.disabled = true;
84
+ input.required = false;
85
+
86
+ // The setProps() helper: useful for setting multiple properties at once using a configuration object.
87
+ input.setProps({
88
+ value: "Dynamic Value",
89
+ placeholder: "Updated Placeholder",
90
+ readOnly: true
91
+ });
92
+ ```
93
+
94
+ -----
95
+
96
+ ## 3\. Styling & Theming
97
+
98
+ The component uses **Shadow DOM**, which means internal elements (like the actual `<input>` tag) are isolated from global CSS. You cannot style internal classes like `.fc-input-field` directly. Instead, you use **CSS Variables** or style the **Host Component**.
99
+
100
+ ### Strategy A: CSS Variables (Theming)
101
+
102
+ Variables pierce the Shadow DOM and are the primary way to theme the component.
103
+
104
+ ```css
105
+ /* Apply globally to all inputs */
106
+ :root {
107
+ --fc-input-bg: #ffffff;
108
+ --fc-input-border: #ccc;
109
+ --fc-input-focus-ring: 0 0 0 3px rgba(0, 123, 255, 0.25);
110
+ --fc-input-radius: 4px;
111
+ }
112
+
113
+ /* Apply only on a theme */
114
+ :root[fc-theme="dark"] {
115
+ --fc-input-bg: #333;
116
+ --fc-input-fg: #fff;
117
+ }
118
+
119
+ /* Apply to a specific class */
120
+ .username-input {
121
+ --fc-input-border: #555;
122
+ }
123
+ ```
124
+
125
+ ### Strategy B: Host Styling
126
+
127
+ You can style the element tag itself (dimensions, margins, display).
128
+
129
+ ```css
130
+ /* Target by tag name */
131
+ fc-input {
132
+ display: block;
133
+ margin-bottom: 1rem;
134
+ width: 100%;
135
+ max-width: 400px;
136
+ }
137
+
138
+ /* Target by class */
139
+ .my-custom-input {
140
+ width: 50%;
141
+ }
142
+
143
+ /* Target specific states provided by the component */
144
+ fc-input[disabled] {
145
+ opacity: 0.7;
146
+ }
147
+
148
+ fc-input[touched]:invalid {
149
+ /* You can style the host wrapper when error exists */
150
+ animation: shake 0.2s ease-in-out;
151
+ }
152
+ ```
153
+
154
+ -----
155
+
156
+ ## 4\. Events
157
+
158
+ Each element has its own unique events that are emitted from it. Even though the elements still standart events like `input`, `click`, `blur`, because of the nature of the Shadow DOM, it is highly recommended to listen to only custom Events.
159
+
160
+ ```javascript
161
+ const el = document.querySelector('fc-input');
162
+
163
+ el.addEventListener('fc-input', (e) => {
164
+ console.log('Typing:', e.detail.value);
165
+ });
166
+
167
+ el.addEventListener('fc-change', (e) => {
168
+ console.log('Committed:', e.detail.value);
169
+ });
170
+ ```
171
+
172
+ -----
173
+
174
+ ## 5\. Validation Logic
175
+
176
+ Some elements has validation logic, most are form field elements, link inputs, select, combobox, etc. Visual errors (red borders) are **suppressed** until the user interacts with the field (blur) or submits the form. Validation works in two layers:
177
+
178
+ ### Layer 1: Native Attributes
179
+
180
+ Standard HTML5 attributes work automatically.
181
+
182
+ * `required`, `min`, `max`, `step`
183
+ * `minlength`, `maxlength`, `pattern`
184
+ * `type="email"`, `type="url"`
185
+
186
+
187
+ ### Layer 2: Custom Validator Function
188
+
189
+ You can inject custom business logic using the `.validator` property.
190
+
191
+ ```javascript
192
+ const usernameInput = document.getElementById('user-field');
193
+
194
+ // Function receives value -> returns Error String or Null
195
+ usernameInput.validator = (value) => {
196
+ if (value.toLowerCase() === 'admin') {
197
+ return 'This username is reserved.'; // Error message
198
+ }
199
+ return null; // Valid
200
+ };
201
+ ```
202
+
203
+ On both ways, you can also use our custom `<fc-error>` component to display the error message as a paragraph:
204
+
205
+ ```html
206
+ <fc-input
207
+ id="email-field"
208
+ label="Email"
209
+ type="email"
210
+ disabled
211
+ placeholder="john@doe.com"
212
+ >
213
+ </fc-input>
214
+ <fc-error
215
+ for="email-field"
216
+ >
217
+ </fc-error>
218
+ ```
219
+
220
+ -----
221
+
222
+ ## 6\. Accessibility (A11y)
223
+
224
+ * **Delegates Focus:** Most elements has delegate focus, which means clicking anywhere on the component focuses the internal input.
225
+ * **ARIA Labels:** `aria-label`, `aria-labelledby`, `aria-describedby` and others aria's can be set directly to the fc-element. The browser automatically maps them to the respective internal element.
226
+ * **Auto-States:** `aria-disabled`, `aria-required`, and `aria-invalid` are automatically managed by `ElementInternals`. Do not set these manually.
227
+ * **Other:** Other aria properties are added directly to the element template because they do not need to be custom and does not change. Example: `<fc-combobox>` has keyboard mapping arias added to its template, `<fc-input>` has 'aria-live' on its internal input element, etc.
228
+
229
+ -----
230
+
231
+ ## 7\. Attribute Reflection
232
+
233
+ For all properties (`disabled`, `placeholder`, `min`, etc.), the component uses **Reflection**.
234
+
235
+ * **JS to HTML:** Setting `el.disabled = true` adds the `disabled` attribute to the HTML tag.
236
+ * **HTML to JS:** Changing the HTML attribute triggers `attributeChangedCallback`, which updates the internal logic.
package/dist/index.css CHANGED
@@ -75,34 +75,34 @@
75
75
  :root {
76
76
  font-family: var(--fc-font-family);
77
77
  --fc-combobox-bg: var(--fc-white);
78
- --fc-combobox-disabled-bg: var(--fc-gray-50);
78
+ --fc-combobox-bg-disabled: var(--fc-gray-50);
79
79
  --fc-combobox-fg: var(--fc-gray-900);
80
80
  --fc-combobox-border: var(--fc-gray-300);
81
81
  --fc-combobox-border-hover: var(--fc-gray-400);
82
82
  --fc-combobox-border-focus: var(--fc-primary-400);
83
83
  --fc-combobox-placeholder: var(--fc-gray-400);
84
- --fc-combobox-disabled-placeholder: var(--fc-gray-300);
84
+ --fc-combobox-placeholder-disabled: var(--fc-gray-300);
85
85
  --fc-combobox-focus-ring: var(--fc-focus-ring-sm) var(--fc-focus-ring-primary);
86
- --fc-combobox-error-bg: var(--fc-danger-50);
87
- --fc-combobox-error-color: var(--fc-danger-300);
88
- --fc-combobox-error-focus-ring: var(--fc-focus-ring-danger);
86
+ --fc-combobox-bg-error: var(--fc-danger-50);
87
+ --fc-combobox-border-error: var(--fc-danger-300);
88
+ --fc-combobox-focus-ring-error: var(--fc-focus-ring-danger);
89
89
  --fc-combobox-max-width: 412px;
90
90
  --fc-combobox-padding: var(--fc-space-3);
91
91
  --fc-combobox-border-width: var(--fc-border-width-xs);
92
92
  --fc-combobox-radius: var(--fc-radius-md);
93
93
  --fc-combobox-shadow: var(--fc-shadow-none);
94
94
  --fc-option-bg: var(--fc-white);
95
- --fc-option-disabled-bg: var(--fc-gray-50);
95
+ --fc-option-bg-disabled: var(--fc-gray-50);
96
96
  --fc-option-bg-hover: var(--fc-gray-100);
97
97
  --fc-option-bg-selected: var(--fc-primary-200);
98
98
  --fc-option-bg-active: var(--fc-primary-100);
99
99
  --fc-option-fg: var(--fc-gray-900);
100
- --fc-option-disabled-fg: var(--fc-gray-300);
100
+ --fc-option-fg-disabled: var(--fc-gray-300);
101
101
  --fc-option-fg-selected: var(--fc-primary-700);
102
102
  --fc-option-padding: var(--fc-space-3);
103
103
  --fc-option-radius: var(--fc-radius-md);
104
104
  --fc-input-bg: var(--fc-white);
105
- --fc-input-disabled-bg: var(--fc-gray-50);
105
+ --fc-input-bg-disabled: var(--fc-gray-50);
106
106
  --fc-input-fg: var(--fc-gray-900);
107
107
  --fc-input-border: var(--fc-gray-400);
108
108
  --fc-input-border-hover: var(--fc-gray-400);
@@ -110,9 +110,9 @@
110
110
  --fc-input-placeholder: var(--fc-gray-400);
111
111
  --fc-input-disabled-placeholder: var(--fc-gray-300);
112
112
  --fc-input-focus-ring: var(--fc-focus-ring-sm) var(--fc-focus-ring-primary);
113
- --fc-input-error-bg: var(--fc-danger-50);
114
- --fc-input-error-color: var(--fc-danger-300);
115
- --fc-input-error-focus-ring: var(--fc-focus-ring-danger);
113
+ --fc-input-bg-error: var(--fc-danger-50);
114
+ --fc-input-border-error: var(--fc-danger-300);
115
+ --fc-input-focus-ring-error: var(--fc-focus-ring-danger);
116
116
  --fc-input-password-icon-color: var(--fc-gray-400);
117
117
  --fc-input-password-icon-color-hover: var(--fc-gray-500);
118
118
  --fc-input-file-border: var(--fc-gray-400);
@@ -126,45 +126,46 @@
126
126
  --fc-input-border-width: var(--fc-border-width-xs);
127
127
  --fc-input-radius: var(--fc-radius-md);
128
128
  --fc-input-shadow: var(--fc-shadow-none);
129
- --fc-error-color: var(--fc-danger-300);
129
+ --fc-error-fg: var(--fc-danger-300);
130
130
  --fc-error-max-width: fit-content;
131
131
  --fc-error-font-size: var(--fc-font-size-sm);
132
132
  }
133
133
 
134
134
  /* src/styles/themes/fc-theme-light.css */
135
135
  :root[fc-theme=light] {
136
+ --fc-color-scheme: light !important;
136
137
  --fc-combobox-bg: var(--fc-white);
137
- --fc-combobox-disabled-bg: var(--fc-gray-50);
138
+ --fc-combobox-bg-disabled: var(--fc-gray-50);
138
139
  --fc-combobox-fg: var(--fc-gray-900);
139
140
  --fc-combobox-border: var(--fc-gray-300);
140
141
  --fc-combobox-border-hover: var(--fc-gray-400);
141
142
  --fc-combobox-border-focus: var(--fc-primary-400);
142
143
  --fc-combobox-placeholder: var(--fc-gray-400);
143
- --fc-combobox-disabled-placeholder: var(--fc-gray-300);
144
+ --fc-combobox-placeholder-disabled: var(--fc-gray-300);
144
145
  --fc-combobox-focus-ring: var(--fc-focus-ring-sm) var(--fc-focus-ring-primary);
145
- --fc-combobox-error-bg: var(--fc-danger-50);
146
- --fc-combobox-error-color: var(--fc-danger-300);
147
- --fc-combobox-error-focus-ring: var(--fc-focus-ring-danger);
146
+ --fc-combobox-bg-error: var(--fc-danger-50);
147
+ --fc-combobox-border-error: var(--fc-danger-300);
148
+ --fc-combobox-focus-ring-error: var(--fc-focus-ring-danger);
148
149
  --fc-option-bg: var(--fc-white);
149
- --fc-option-disabled-bg: var(--fc-gray-50);
150
+ --fc-option-bg-disabled: var(--fc-gray-50);
150
151
  --fc-option-bg-hover: var(--fc-gray-100);
151
152
  --fc-option-bg-selected: var(--fc-primary-200);
152
153
  --fc-option-bg-active: var(--fc-primary-100);
153
154
  --fc-option-fg: var(--fc-gray-900);
154
- --fc-option-disabled-fg: var(--fc-gray-300);
155
+ --fc-option-fg-disabled: var(--fc-gray-300);
155
156
  --fc-option-fg-selected: var(--fc-primary-700);
156
157
  --fc-input-bg: var(--fc-white);
157
- --fc-input-disabled-bg: var(--fc-gray-50);
158
+ --fc-input-bg-disabled: var(--fc-gray-50);
158
159
  --fc-input-fg: var(--fc-gray-900);
159
160
  --fc-input-border: var(--fc-gray-300);
160
161
  --fc-input-border-hover: var(--fc-gray-400);
161
162
  --fc-input-border-focus: var(--fc-primary-400);
162
163
  --fc-input-placeholder: var(--fc-gray-400);
163
- --fc-input-disabled-placeholder: var(--fc-gray-300);
164
+ --fc-input-placeholder-disabled: var(--fc-gray-300);
164
165
  --fc-input-focus-ring: var(--fc-focus-ring-sm) var(--fc-focus-ring-primary);
165
- --fc-input-error-bg: var(--fc-danger-50);
166
- --fc-input-error-color: var(--fc-danger-300);
167
- --fc-input-error-focus-ring: var(--fc-focus-ring-danger);
166
+ --fc-input-bg-error: var(--fc-danger-50);
167
+ --fc-input-border-error: var(--fc-danger-300);
168
+ --fc-input-focus-ring-error: var(--fc-focus-ring-danger);
168
169
  --fc-input-password-icon-color: var(--fc-gray-400);
169
170
  --fc-input-password-icon-color-hover: var(--fc-gray-500);
170
171
  --fc-input-file-border: var(--fc-gray-300);
@@ -178,43 +179,44 @@
178
179
  --fc-input-border-width: var(--fc-border-width-xs);
179
180
  --fc-input-radius: var(--fc-radius-md);
180
181
  --fc-input-shadow: var(--fc-shadow-none);
181
- --fc-error-color: var(--fc-danger-300);
182
+ --fc-error-fg: var(--fc-danger-300);
182
183
  }
183
184
 
184
185
  /* src/styles/themes/fc-theme-dark.css */
185
186
  :root[fc-theme=dark] {
187
+ --fc-color-scheme: dark !important;
186
188
  --fc-combobox-bg: var(--fc-gray-800);
187
- --fc-combobox-disabled-bg: var(--fc-gray-900);
189
+ --fc-combobox-bg-disabled: var(--fc-gray-900);
188
190
  --fc-combobox-fg: var(--fc-gray-200);
189
191
  --fc-combobox-border: var(--fc-gray-700);
190
192
  --fc-combobox-border-hover: var(--fc-gray-500);
191
193
  --fc-combobox-border-focus: var(--fc-primary-400);
192
194
  --fc-combobox-placeholder: var(--fc-gray-500);
193
- --fc-combobox-disabled-placeholder: var(--fc-gray-700);
195
+ --fc-combobox-placeholder-disabled: var(--fc-gray-700);
194
196
  --fc-combobox-focus-ring: var(--fc-focus-ring-sm) var(--fc-focus-ring-secondary);
195
- --fc-combobox-error-bg: var(--fc-danger-900);
196
- --fc-combobox-error-color: var(--fc-danger-700);
197
- --fc-combobox-error-focus-ring: var(--fc-focus-ring-danger-secondary);
197
+ --fc-combobox-bg-error: var(--fc-danger-900);
198
+ --fc-combobox-border-error: var(--fc-danger-700);
199
+ --fc-combobox-focus-ring-error: var(--fc-focus-ring-danger-secondary);
198
200
  --fc-option-bg: var(--fc-gray-800);
199
- --fc-option-disabled-bg: var(--fc-gray-900);
201
+ --fc-option-bg-disabled: var(--fc-gray-900);
200
202
  --fc-option-bg-hover: var(--fc-gray-700);
201
203
  --fc-option-bg-active: var(--fc-primary-500);
202
204
  --fc-option-bg-selected: var(--fc-primary-600);
203
205
  --fc-option-fg: var(--fc-gray-200);
204
- --fc-option-disabled-fg: var(--fc-gray-700);
206
+ --fc-option-fg-disabled: var(--fc-gray-700);
205
207
  --fc-option-fg-selected: var(--fc-primary-50);
206
208
  --fc-input-bg: var(--fc-gray-800);
207
- --fc-input-disabled-bg: var(--fc-gray-900);
209
+ --fc-input-bg-disabled: var(--fc-gray-900);
208
210
  --fc-input-fg: var(--fc-gray-200);
209
211
  --fc-input-border: var(--fc-gray-700);
210
212
  --fc-input-border-hover: var(--fc-gray-500);
211
213
  --fc-input-border-focus: var(--fc-primary-400);
212
214
  --fc-input-placeholder: var(--fc-gray-500);
213
- --fc-input-disabled-placeholder: var(--fc-gray-700);
215
+ --fc-input-placeholder-disabled: var(--fc-gray-700);
214
216
  --fc-input-focus-ring: var(--fc-focus-ring-sm) var(--fc-focus-ring-secondary);
215
- --fc-input-error-bg: var(--fc-danger-900);
216
- --fc-input-error-color: var(--fc-danger-700);
217
- --fc-input-error-focus-ring: var(--fc-focus-ring-danger-secondary);
217
+ --fc-input-bg-error: var(--fc-danger-900);
218
+ --fc-input-border-error: var(--fc-danger-700);
219
+ --fc-input-focus-ring-error: var(--fc-focus-ring-danger-secondary);
218
220
  --fc-input-password-icon-color: var(--fc-gray-500);
219
221
  --fc-input-password-icon-color-hover: var(--fc-gray-400);
220
222
  --fc-input-file-border: var(--fc-gray-700);
@@ -228,5 +230,5 @@
228
230
  --fc-input-border-width: var(--fc-border-width-xs);
229
231
  --fc-input-radius: var(--fc-radius-md);
230
232
  --fc-input-shadow: var(--fc-shadow-none);
231
- --fc-error-color: var(--fc-danger-700);
233
+ --fc-error-fg: var(--fc-danger-700);
232
234
  }
package/dist/index.d.ts CHANGED
@@ -51,7 +51,6 @@ declare class FcCombobox extends HTMLElement {
51
51
  private onOptionSelect;
52
52
  private onOutsideClick;
53
53
  private onFocusOut;
54
- private onDropdownClick;
55
54
  private onFocus;
56
55
  private onSlotChange;
57
56
  private onKeyDown;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- var styles = `\n\t:host {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n \tfont-family: var(--fc-font-family);\n\t\tmax-width: var(--fc-combobox-max-width);\n\t}\n\n\t/* only show invalid style if the user has touched the field (blurred). the :invalid pseudo-class comes from \n\tinternals.setValidity() logic. */\n\n\t:host([touched]:invalid) .fc-input {\n background-color: var(--fc-combobox-error-bg);\n border-color: var(--fc-combobox-error-color);\n }\n\n :host([touched]:invalid) .fc-input:focus {\n box-shadow: 0 0 0 2px var(--fc-combobox-error-focus-ring);\n }\n\n\t.fc-input {\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n\n\t\tpadding: var(--fc-combobox-padding);\n\t\tborder-radius: var(--fc-combobox-radius);\n\t\tbackground: var(--fc-combobox-bg);\n\t\tcolor: var(--fc-combobox-fg);\n\n\t\tborder: var(--fc-combobox-border-width) solid var(--fc-combobox-border);\n\t\tfont-size: var(--fc-font-size-md);\n\n\t\tbox-shadow: var(--fc-combobox-shadow);\n\t\ttransition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\t}\n\n\t.fc-input::placeholder {\n\t\tcolor: var(--fc-combobox-placeholder);\n\t}\n\n\t.fc-input:hover {\n\t\tborder-color: var(--fc-combobox-border-hover);\n\t}\n\n\t.fc-input:focus {\n\t\tborder-color: var(--fc-combobox-border-focus);\n\t\toutline: none;\n\t\tbox-shadow: var(--fc-combobox-focus-ring);\n\t}\n\n\t.fc-options {\n\t\tposition: absolute;\n\t\ttop: calc(100% + 6px);\n\t\tleft: 0;\n\t\tright: 0;\n\t\tz-index: 1000;\n\t\tbackground: var(--fc-combobox-dropdown-bg, var(--fc-combobox-bg));\n\t\tborder: var(--fc-combobox-border-width) solid var(--fc-combobox-border);\n\t\tborder-radius: var(--fc-combobox-dropdown-radius, var(--fc-combobox-radius));\n\t\tpadding: var(--fc-combobox-dropdown-padding, calc(var(--fc-combobox-padding) - 5px));\n\t\tbox-shadow: var(--fc-combobox-dropdown-shadow);\n\t\tmax-height: var(--fc-combobox-dropdown-max-height, 240px);\n\t\toverflow-y: auto;\n\t\tbox-sizing: border-box;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 4px;\n\t}\n\t\n\t.fc-options[hidden] {\n display: none !important;\n }\n\n\t:host([disabled]) {\n cursor: not-allowed;\n }\n\n\t.fc-input:disabled {\n\t\tbackground: var(--fc-combobox-disabled-bg);\n cursor: not-allowed;\n\t\tbox-shadow: none;\n }\n\n\t.fc-input:disabled::placeholder {\n\t\tcolor: var(--fc-combobox-disabled-placeholder);\n }\n\n\t.fc-input:disabled:hover {\n\t\tborder-color: var(--fc-combobox-border);\n\t}\n\n\t.fc-input:disabled:focus {\n\t\tborder-color: var(--fc-combobox-border);\n\t}\n`;
1
+ var styles = `\n\t:host {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n \tfont-family: var(--fc-font-family);\n\t\tmax-width: var(--fc-combobox-max-width);\n\t}\n\n\t/* only show invalid style if the user has touched the field (blurred). the :invalid pseudo-class comes from \n\tinternals.setValidity() logic. */\n\n\t:host([touched]:invalid) .fc-input {\n background-color: var(--fc-combobox-bg-error);\n border-color: var(--fc-combobox-border-error);\n }\n\n :host([touched]:invalid) .fc-input:focus {\n box-shadow: 0 0 0 2px var(--fc-combobox-focus-ring-error);\n }\n\n\t.fc-input {\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n\n\t\tpadding: var(--fc-combobox-padding);\n\t\tborder-radius: var(--fc-combobox-radius);\n\t\tbackground: var(--fc-combobox-bg);\n\t\tcolor: var(--fc-combobox-fg);\n\n\t\tborder: var(--fc-combobox-border-width) solid var(--fc-combobox-border);\n\t\tfont-size: var(--fc-font-size-md);\n\n\t\tbox-shadow: var(--fc-combobox-shadow);\n\t\ttransition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\t}\n\n\t.fc-input::placeholder {\n\t\tcolor: var(--fc-combobox-placeholder);\n\t}\n\n\t.fc-input:hover {\n\t\tborder-color: var(--fc-combobox-border-hover);\n\t}\n\n\t.fc-input:focus {\n\t\tborder-color: var(--fc-combobox-border-focus);\n\t\toutline: none;\n\t\tbox-shadow: var(--fc-combobox-focus-ring);\n\t}\n\n\t.fc-options {\n\t\tposition: absolute;\n\t\ttop: calc(100% + 6px);\n\t\tleft: 0;\n\t\tright: 0;\n\t\tz-index: 1000;\n\t\tbackground: var(--fc-combobox-dropdown-bg, var(--fc-combobox-bg));\n\t\tborder: var(--fc-combobox-border-width) solid var(--fc-combobox-border);\n\t\tborder-radius: var(--fc-combobox-dropdown-radius, var(--fc-combobox-radius));\n\t\tpadding: var(--fc-combobox-dropdown-padding, calc(var(--fc-combobox-padding) - 5px));\n\t\tbox-shadow: var(--fc-combobox-dropdown-shadow);\n\t\tmax-height: var(--fc-combobox-dropdown-max-height, 240px);\n\t\toverflow-y: auto;\n\t\tbox-sizing: border-box;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 4px;\n\t}\n\t\n\t.fc-options[hidden] {\n display: none !important;\n }\n\n\t:host([disabled]) {\n cursor: not-allowed;\n }\n\n\t.fc-input:disabled {\n\t\tbackground: var(--fc-combobox-bg-disabled);\n cursor: not-allowed;\n\t\tbox-shadow: none;\n }\n\n\t.fc-input:disabled::placeholder {\n\t\tcolor: var(--fc-combobox-placeholder-disabled);\n }\n\n\t.fc-input:disabled:hover {\n\t\tborder-color: var(--fc-combobox-border);\n\t}\n\n\t.fc-input:disabled:focus {\n\t\tborder-color: var(--fc-combobox-border);\n\t}\n`;
2
2
 
3
3
  var template = document.createElement("template");
4
4
 
5
- template.innerHTML = `\n\t<style>${styles}</style>\n\n\t<input \n\t\tid="fc-input" \n\t\tclass="fc-input"\n\t\ttype="text" \n\t\trole="combobox"\n\t\taria-autocomplete="list"\n\t\taria-expanded="false"\n\t\taria-haspopup="listbox"\n\t\taria-controls="fc-options"\n\t\tpart="input"\n\t\tautocomplete="off"\n\t/>\n\n\t<div \n\t\tid="fc-options" \n\t\tpart="options" \n\t\tclass="fc-options" \n\t\trole="listbox"\n\t\thidden\n\t>\n\t\t<slot></slot>\n\t</div>\n\t\n`;
5
+ template.innerHTML = `\n\t<style>${styles}</style>\n\n\t<input \n\t\tclass="fc-input"\n\t\ttype="text" \n\t\trole="combobox"\n\t\taria-autocomplete="list"\n\t\taria-expanded="false"\n\t\taria-haspopup="listbox"\n\t\taria-controls="fc-options"\n\t\tpart="input"\n\t\tautocomplete="off"\n\t/>\n\n\t<div \n\t\tpart="options" \n\t\tclass="fc-options" \n\t\trole="listbox"\n\t\thidden\n\t>\n\t\t<slot></slot>\n\t</div>\n\t\n`;
6
6
 
7
7
  var FcCombobox = class extends HTMLElement {
8
8
  constructor() {
@@ -16,13 +16,20 @@ var FcCombobox = class extends HTMLElement {
16
16
  });
17
17
  shadow.appendChild(template.content.cloneNode(true));
18
18
  this.internals = this.attachInternals();
19
+ this.inputEl = shadow.querySelector(".fc-input");
20
+ this.dropdownEl = shadow.querySelector(".fc-options");
21
+ const randomId = Math.random().toString(36).substring(2, 9);
22
+ const inputId = `fc-input-${randomId}`;
23
+ const dropdownId = `fc-options-${randomId}`;
24
+ this.inputEl.id = inputId;
25
+ this.dropdownEl.id = dropdownId;
26
+ this.inputEl.setAttribute("aria-controls", dropdownId);
19
27
  this.onInput = this.onInput.bind(this);
20
28
  this.onChange = this.onChange.bind(this);
21
29
  this.onOptionSelect = this.onOptionSelect.bind(this);
22
30
  this.onOutsideClick = this.onOutsideClick.bind(this);
23
31
  this.onFocusOut = this.onFocusOut.bind(this);
24
32
  this.onFocus = this.onFocus.bind(this);
25
- this.onDropdownClick = this.onDropdownClick.bind(this);
26
33
  this.onSlotChange = this.onSlotChange.bind(this);
27
34
  this.onKeyDown = this.onKeyDown.bind(this);
28
35
  this.onBlur = this.onBlur.bind(this);
@@ -82,7 +89,7 @@ var FcCombobox = class extends HTMLElement {
82
89
  if (!this.inputEl) {
83
90
  return;
84
91
  }
85
- const options = Array.from(this.querySelectorAll("fc-option"));
92
+ const options = this.querySelectorAll("fc-option");
86
93
  options.forEach(option => {
87
94
  const selected = option.value === newValue;
88
95
  option.selected = selected;
@@ -162,12 +169,10 @@ var FcCombobox = class extends HTMLElement {
162
169
  this.syncValidity();
163
170
  }
164
171
  connectedCallback() {
165
- this.inputEl = this.shadowRoot.getElementById("fc-input");
166
- this.dropdownEl = this.shadowRoot.getElementById("fc-options");
172
+ this.internals.setFormValue(this.value);
167
173
  if (this.hasAttribute("placeholder")) {
168
174
  this.inputEl.placeholder = this.getAttribute("placeholder");
169
175
  }
170
- this.internals.setFormValue(this.value);
171
176
  if (this.hasAttribute("disabled")) {
172
177
  this.inputEl.disabled = true;
173
178
  this.internals.ariaDisabled = "true";
@@ -185,7 +190,6 @@ var FcCombobox = class extends HTMLElement {
185
190
  document.addEventListener("click", this.onOutsideClick);
186
191
  this.addEventListener("focusout", this.onFocusOut);
187
192
  this.inputEl.addEventListener("focus", this.onFocus);
188
- this.dropdownEl.addEventListener("mousedown", this.onDropdownClick);
189
193
  this.inputEl.addEventListener("blur", this.onBlur);
190
194
  this.addEventListener("invalid", this.onInvalid);
191
195
  const slot = this.shadowRoot.querySelector("slot");
@@ -233,7 +237,7 @@ var FcCombobox = class extends HTMLElement {
233
237
  this.inputEl.value = "";
234
238
  }
235
239
  this.internals.setFormValue("");
236
- const options = Array.from(this.querySelectorAll("fc-option"));
240
+ const options = this.querySelectorAll("fc-option");
237
241
  options.forEach(option => {
238
242
  option.selected = false;
239
243
  option.hidden = false;
@@ -251,19 +255,20 @@ var FcCombobox = class extends HTMLElement {
251
255
  if (restoredValue) {
252
256
  this._value = restoredValue;
253
257
  this.internals.setFormValue(restoredValue);
254
- const options = Array.from(this.querySelectorAll("fc-option"));
255
- const match = options.find(opt => opt.value === restoredValue);
256
- if (match && this.inputEl) {
257
- this.inputEl.value = match.label;
258
- match.selected = true;
259
- }
258
+ const options = this.querySelectorAll("fc-option");
259
+ options.forEach(option => {
260
+ if (option.value === restoredValue && this.inputEl) {
261
+ this.inputEl.value = option.label;
262
+ option.selected = true;
263
+ }
264
+ });
260
265
  this.syncValidity();
261
266
  }
262
267
  }
263
268
  onInput(e) {
264
269
  const rawQuery = e.target.value;
265
270
  const query = rawQuery.toLowerCase().trim();
266
- const options = Array.from(this.querySelectorAll("fc-option"));
271
+ const options = this.querySelectorAll("fc-option");
267
272
  if (query.length === 0) {
268
273
  options.forEach(opt => {
269
274
  opt.hidden = false;
@@ -338,7 +343,7 @@ var FcCombobox = class extends HTMLElement {
338
343
  this.inputEl.value = label;
339
344
  this._value = value;
340
345
  this.internals.setFormValue(value);
341
- const options = Array.from(this.querySelectorAll("fc-option"));
346
+ const options = this.querySelectorAll("fc-option");
342
347
  options.forEach(option => {
343
348
  const selected = option.value === value;
344
349
  option.selected = selected;
@@ -366,9 +371,6 @@ var FcCombobox = class extends HTMLElement {
366
371
  this.toggleDropdown(false);
367
372
  }
368
373
  }
369
- onDropdownClick(e) {
370
- e.preventDefault();
371
- }
372
374
  onFocus(e) {
373
375
  if (this.disabled) {
374
376
  return;
@@ -385,7 +387,7 @@ var FcCombobox = class extends HTMLElement {
385
387
  if (!this._value) {
386
388
  return;
387
389
  }
388
- const options = Array.from(this.querySelectorAll("fc-option"));
390
+ const options = this.querySelectorAll("fc-option");
389
391
  let foundMatch = false;
390
392
  options.forEach(option => {
391
393
  const selected = option.value === this._value;
@@ -463,7 +465,7 @@ var FcCombobox = class extends HTMLElement {
463
465
  this.inputEl.value = label;
464
466
  this._value = value;
465
467
  this.internals.setFormValue(value);
466
- const allOptions = Array.from(this.querySelectorAll("fc-option"));
468
+ const allOptions = this.querySelectorAll("fc-option");
467
469
  allOptions.forEach(opt => {
468
470
  const selected = opt.value === value;
469
471
  opt.selected = selected;
@@ -511,8 +513,13 @@ var FcCombobox = class extends HTMLElement {
511
513
  return;
512
514
  }
513
515
  if (this.strict && this._value) {
514
- const options = Array.from(this.querySelectorAll("fc-option"));
515
- const match = options.some(opt => opt.value === this._value);
516
+ const options = this.querySelectorAll("fc-option");
517
+ let match = false;
518
+ options.forEach(option => {
519
+ if (option.value === this._value) {
520
+ match = true;
521
+ }
522
+ });
516
523
  if (!match) {
517
524
  this.internals.setValidity({
518
525
  customError: true
@@ -554,7 +561,7 @@ var defineCombobox = () => {
554
561
  return FcCombobox;
555
562
  };
556
563
 
557
- var styles2 = `\n\t:host {\n\t\tdisplay: block;\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n \tfont-family: var(--fc-font-family);\n\t}\n\n\t:host([hidden]) {\n display: none !important;\n }\n\n\t:host([disabled]) {\n cursor: not-allowed;\n }\n\t\t\n\t\t\n\tbutton.fc-option {\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n\t\ttext-align: left;\n\t\tbackground: var(--fc-option-bg);\n\t\tcolor: var(--fc-option-fg);\n\t\tpadding: var(--fc-option-padding);\n\t\tborder-radius: var(--fc-option-radius);\n\t\tborder: none;\n\t\tfont: inherit;\n\t\tcursor: pointer;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t}\n\n\tbutton.fc-option:hover {\n\t\tbackground: var(--fc-option-bg-hover);\n\t\ttransition: background 0.15s ease-in-out, color 0.15s ease-in-out;\n\t}\n\n\tbutton.fc-option[data-active="true"] { \n background: var(--fc-option-bg-active);\n }\n\n\tbutton.fc-option:disabled {\n\t\tcolor: var(--fc-option-disabled-fg);\n\t\tbackground: var(--fc-option-disabled-bg);\n\t\tpointer-events: none; // this prevents disabled attribute on button to move focus out of the fc-combobox\n\t\tbox-shadow: none;\n\t}\n\n button.fc-option:disabled:hover {\n\t\tbackground: var(--fc-option-disabled-bg);\n\t}\n\n\tbutton.fc-option[aria-selected="true"] {\n\t\tbackground: var(--fc-option-bg-selected);\n\t\tcolor: var(--fc-option-fg-selected);\n\t}\n\n`;
564
+ var styles2 = `\n\t:host {\n\t\tdisplay: block;\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n \tfont-family: var(--fc-font-family);\n\t}\n\n\t:host([hidden]) {\n display: none !important;\n }\n\n\t:host([disabled]) {\n cursor: not-allowed;\n }\n\t\t\n\t\t\n\tbutton.fc-option {\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n\t\ttext-align: left;\n\t\tbackground: var(--fc-option-bg);\n\t\tcolor: var(--fc-option-fg);\n\t\tpadding: var(--fc-option-padding);\n\t\tborder-radius: var(--fc-option-radius);\n\t\tborder: none;\n\t\tfont: inherit;\n\t\tcursor: pointer;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t}\n\n\tbutton.fc-option:hover {\n\t\tbackground: var(--fc-option-bg-hover);\n\t\ttransition: background 0.15s ease-in-out, color 0.15s ease-in-out;\n\t}\n\n\tbutton.fc-option[data-active="true"] { \n background: var(--fc-option-bg-active);\n }\n\n\tbutton.fc-option:disabled {\n\t\tcolor: var(--fc-option-fg-disabled);\n\t\tbackground: var(--fc-option-bg-disabled);\n\t\tpointer-events: none; // this prevents disabled attribute on button to move focus out of the fc-combobox\n\t\tbox-shadow: none;\n\t}\n\n button.fc-option:disabled:hover {\n\t\tbackground: var(--fc-option-bg-disabled);\n\t}\n\n\tbutton.fc-option[aria-selected="true"] {\n\t\tbackground: var(--fc-option-bg-selected);\n\t\tcolor: var(--fc-option-fg-selected);\n\t}\n\n`;
558
565
 
559
566
  var template2 = document.createElement("template");
560
567
 
@@ -694,11 +701,11 @@ var defineOption = () => {
694
701
  return FcOption;
695
702
  };
696
703
 
697
- var styles3 = `\n\t:host {\n\t\tdisplay: block;\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n \tfont-family: var(--fc-font-family);\n\t\tmax-width: var(--fc-input-max-width);\n\t}\n\n\t:host([disabled]) {\n\t\tcursor: not-allowed;\n\t}\n\n\t:host([hidden]) {\n\t\tdisplay: none !important;\n\t}\n\n\t/* only show invalid style if the user has touched the field (blurred). the :invalid pseudo-class comes from \n\tinternals.setValidity() logic. */\n\n\t:host([touched]:invalid) .fc-input-field {\n\t\tbackground-color: var(--fc-input-error-bg);\n\t\tborder-color: var(--fc-input-error-color);\n\t}\n\n\t:host([touched]:invalid) .fc-input-field:focus {\n\t\tbox-shadow: 0 0 0 2px var(--fc-input-error-focus-ring);\n\t}\n\n\t.fc-input-wrapper {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\twidth: 100%;\n\t}\n\n\t.fc-input-field {\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n\n\t\tpadding: var(--fc-input-padding);\n\t\tborder-radius: var(--fc-input-radius);\n\t\tbackground: var(--fc-input-bg);\n\t\tcolor: var(--fc-input-fg);\n\n\t\tborder: var(--fc-input-border-width) solid var(--fc-input-border);\n\n\t\tfont-size: var(--fc-font-size-md);\n\n\t\tbox-shadow: var(--fc-input-shadow);\n\t\ttransition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\t\tfont-family: inherit;\n\n\t\t-webkit-appearance: none;\n\t\tappearance: none;\n\t}\n\n\t.fc-input-field::placeholder {\n\t\tcolor: var(--fc-input-placeholder);\n\t}\n\n\t.fc-input-field:hover {\n\t\tborder-color: var(--fc-input-border-hover);\n\t}\n\n\t.fc-input-field:focus {\n\t\tborder-color: var(--fc-input-border-focus);\n\t\toutline: none;\n\t\tbox-shadow: var(--fc-input-focus-ring);\n\t}\n\n\t.fc-input-field:disabled {\n\t\tbackground: var(--fc-input-disabled-bg);\n\t\tcursor: not-allowed;\n\t\tbox-shadow: none;\n\t}\n\n\t.fc-input-field:disabled::placeholder {\n\t\tcolor: var(--fc-input-disabled-placeholder);\n }\n\n\t/* FILE type specific CSS */\n\n\t.fc-input-field[type="file"] {\n\t\tpadding: calc(var(--fc-input-padding));\n\t\tcursor: pointer;\n\t\tdisplay: flex; \n \talign-items: center;\n\t\tborder-color: var(--fc-input-file-border);\n\t\ttransition: border-color 0.15s ease-in-out, color 0.15s ease-in-out;\n\t}\n\n\t.fc-input-field[type="file"]:focus {\n\t\tborder-color: var(--fc-input-border-focus);\n\t\toutline: none;\n\t\tbox-shadow: var(--fc-input-focus-ring);\n\t}\n\n\t/* target the button inside type="file" */\n\n\t.fc-input-field::file-selector-button {\n\t\tpadding: 4px 10px;\n\t\tborder-radius: var(--fc-input-radius);\n\t\tbackground-color: var(--fc-input-btn-bg);\n\t\tcolor: var(--fc-input-file-btn-fg);\n\t\tborder: 1px solid var(--fc-input-file-border);\n\t\tcursor: pointer;\n\t\tfont-family: inherit;\n\t\ttransition: background-color 0.15s ease;\n\t}\n\n\t/* legacy browsers */\n\t.fc-input-field::-webkit-file-upload-button {\n\t\tmargin-right: 12px;\n\t\tpadding: 4px 10px;\n\t\tborder-radius: var(--fc-input-radius);\n\t\tbackground-color: var(--fc-input-file-btn-bg);\n\t\tcolor: var(--fc-input-file-btn-fg);\n\t\tborder: 1px solid var(--fc-input-file-border);\n\t\tcursor: pointer;\n\t\tfont-family: inherit;\n\t\tfont-size: 0.9em;\n\t}\n\n\t/* Hover effects for the button */\n\n\t.fc-input-field::file-selector-button:hover {\n\t\tbackground-color: var(--fc-input-file-btn-bg-hover); \n\t}\n\n\n\t.fc-input-field::-webkit-file-upload-button:hover {\n\t\tbackground-color: var(--fc-input-file-btn-bg-hover);\n\t}\n\n\t/* PASSWORD type specific CSS */\n\n\t/* when password toggle is visible, add padding to input so text doesn't overlap icon */\n\t:host([type="password"]) .fc-input-field {\n\t\tpadding-right: 40px; \n\t}\n\n\t.fc-password-toggle {\n\t\tposition: absolute;\n\t\tright: 8px; /* position inside the input */\n\t\ttop: 50%;\n\t\ttransform: translateY(-50%);\n\t\tbackground: transparent;\n\t\tborder: none;\n\t\tcursor: pointer;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tcolor: var(--fc-input-password-icon-color);\n\t\ttransition: color 0.15s ease-in-out;\n\t\tz-index: 2; /* above input */\n\t}\n\t\n\t.fc-password-toggle[hidden] {\n display: none !important;\n }\n\n\t.fc-password-toggle:hover {\n\t\tcolor: var(--fc-input-password-icon-color-hover);\n\t}\n\t\n\t.fc-password-toggle svg {\n\t\twidth: 20px;\n\t\theight: 20px;\n\t}\n\n\t.fc-password-toggle svg[hidden] {\n display: none !important;\n }\n\n`;
704
+ var styles3 = `\n\t:host {\n\t\tdisplay: block;\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n \tfont-family: var(--fc-font-family);\n\t\tmax-width: var(--fc-input-max-width);\n\t}\n\n\t:host([disabled]) {\n\t\tcursor: not-allowed;\n\t}\n\n\t:host([hidden]) {\n\t\tdisplay: none !important;\n\t}\n\n\t/* only show invalid style if the user has touched the field (blurred). the :invalid pseudo-class comes from \n\tinternals.setValidity() logic. */\n\n\t:host([touched]:invalid) .fc-input-field {\n\t\tbackground: var(--fc-input-bg-error);\n\t\tborder-color: var(--fc-input-border-error);\n\t}\n\n\t:host([touched]:invalid) .fc-input-field:focus {\n\t\tbox-shadow: 0 0 0 2px var(--fc-input-focus-ring-error);\n\t}\n\n\t.fc-input-wrapper {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\twidth: 100%;\n\t}\n\n\t.fc-input-field {\n\t\twidth: 100%;\n\t\tbox-sizing: border-box;\n\n\t\tpadding: var(--fc-input-padding);\n\t\tborder-radius: var(--fc-input-radius);\n\t\tbackground: var(--fc-input-bg);\n\t\tcolor: var(--fc-input-fg);\n\n\t\tborder: var(--fc-input-border-width) solid var(--fc-input-border);\n\n\t\tfont-size: var(--fc-font-size-md);\n\n\t\tbox-shadow: var(--fc-input-shadow);\n\t\ttransition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\t\tfont-family: inherit;\n\n\t\t-webkit-appearance: none;\n\t\tappearance: none;\n\t}\n\n\t.fc-input-field::placeholder {\n\t\tcolor: var(--fc-input-placeholder);\n\t}\n\n\t.fc-input-field:hover {\n\t\tborder-color: var(--fc-input-border-hover);\n\t}\n\n\t.fc-input-field:focus {\n\t\tborder-color: var(--fc-input-border-focus);\n\t\toutline: none;\n\t\tbox-shadow: var(--fc-input-focus-ring);\n\t}\n\n\t.fc-input-field:disabled {\n\t\tbackground: var(--fc-input-bg-disabled);\n\t\tcursor: not-allowed;\n\t\tbox-shadow: none;\n\t}\n\n\t.fc-input-field:disabled::placeholder {\n\t\tcolor: var(--fc-input-placeholder-disabled);\n }\n\n\t.fc-input-field:disabled:hover {\n\t\tborder-color: var(--fc-input-border);\n\t}\n\n\t.fc-input-field:disabled:focus {\n\t\tborder-color: var(--fc-input-border);\n\t}\n\t\t\n\t/* FILE type specific CSS */\n\n\t.fc-input-field[type="file"] {\n\t\tpadding: calc(var(--fc-input-padding));\n\t\tcursor: pointer;\n\t\tdisplay: flex; \n \talign-items: center;\n\t\tborder-color: var(--fc-input-file-border);\n\t\ttransition: border-color 0.15s ease-in-out, color 0.15s ease-in-out;\n\t}\n\n\t.fc-input-field[type="file"]:focus {\n\t\tborder-color: var(--fc-input-border-focus);\n\t\toutline: none;\n\t\tbox-shadow: var(--fc-input-focus-ring);\n\t}\n\n\t/* target the button inside type="file" */\n\n\t.fc-input-field::file-selector-button {\n\t\tpadding: 4px 10px;\n\t\tborder-radius: var(--fc-input-radius);\n\t\tbackground: var(--fc-input-btn-bg);\n\t\tcolor: var(--fc-input-file-btn-fg);\n\t\tborder: 1px solid var(--fc-input-file-border);\n\t\tcursor: pointer;\n\t\tfont-family: inherit;\n\t\ttransition: background-color 0.15s ease;\n\t}\n\n\t/* legacy browsers */\n\t.fc-input-field::-webkit-file-upload-button {\n\t\tmargin-right: 12px;\n\t\tpadding: 4px 10px;\n\t\tborder-radius: var(--fc-input-radius);\n\t\tbackground: var(--fc-input-file-btn-bg);\n\t\tcolor: var(--fc-input-file-btn-fg);\n\t\tborder: 1px solid var(--fc-input-file-border);\n\t\tcursor: pointer;\n\t\tfont-family: inherit;\n\t\tfont-size: 0.9em;\n\t}\n\n\t/* Hover effects for the button */\n\n\t.fc-input-field::file-selector-button:hover {\n\t\tbackground: var(--fc-input-file-btn-bg-hover); \n\t}\n\n\n\t.fc-input-field::-webkit-file-upload-button:hover {\n\t\tbackground: var(--fc-input-file-btn-bg-hover);\n\t}\n\n\t/* PASSWORD type specific CSS */\n\n\t/* when password toggle is visible, add padding to input so text doesn't overlap icon */\n\t:host([type="password"]) .fc-input-field {\n\t\tpadding-right: 40px; \n\t}\n\n\t.fc-password-toggle {\n\t\tposition: absolute;\n\t\tright: 8px; /* position inside the input */\n\t\ttop: 50%;\n\t\ttransform: translateY(-50%);\n\t\tbackground: transparent;\n\t\tborder: none;\n\t\tcursor: pointer;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tcolor: var(--fc-input-password-icon-color);\n\t\ttransition: color 0.15s ease-in-out;\n\t\tz-index: 2; /* above input */\n\t}\n\t\n\t.fc-password-toggle[hidden] {\n display: none !important;\n }\n\n\t.fc-password-toggle:hover {\n\t\tcolor: var(--fc-input-password-icon-color-hover);\n\t}\n\t\n\t.fc-password-toggle svg {\n\t\twidth: 20px;\n\t\theight: 20px;\n\t}\n\n\t.fc-password-toggle svg[hidden] {\n display: none !important;\n }\n\n`;
698
705
 
699
706
  var template3 = document.createElement("template");
700
707
 
701
- template3.innerHTML = `\n\t<style>${styles3}</style>\n\n\t<div class="fc-input-wrapper">\n\n\t\t\x3c!-- prefix slot here --\x3e\n\t\t\n\t\t<input \n\t\t\tid="fc-field" \n\t\t\tclass="fc-input-field" \n\t\t\tpart="input"\n\t\t\ttype="text"\n\t\t/>\n\n\t\t\x3c!-- password toggle button --\x3e\n\t\t<button \n\t\t\tid="btn-show-pass" \n\t\t\tclass="fc-password-toggle" \n\t\t\tpart="password-toggle" \n\t\t\ttype="button" \n\t\t\thidden \n\t\t\taria-label="Toggle password visibility"\n\t\t\taria-pressed="false"\n\t\t>\n\t\t\t<svg class="icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n\t\t\t\t<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>\n\t\t\t\t<circle cx="12" cy="12" r="3"></circle>\n\t\t\t</svg>\n\t\t\t\n\t\t\t<svg class="icon-eye-off" hidden viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n\t\t\t\t<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>\n\t\t\t\t<line x1="1" y1="1" x2="23" y2="23"></line>\n\t\t\t</svg>\n\t\t</button>\n\n\t\t\x3c!-- suffix slot here --\x3e\n\t</div>\n`;
708
+ template3.innerHTML = `\n\t<style>${styles3}</style>\n\n\t<div class="fc-input-wrapper">\n\n\t\t\x3c!-- prefix slot here --\x3e\n\t\t\n\t\t<input \n\t\t\tclass="fc-input-field" \n\t\t\tpart="input"\n\t\t\ttype="text"\n\t\t/>\n\n\t\t\x3c!-- password toggle button --\x3e\n\t\t<button \n\t\t\tclass="fc-password-toggle" \n\t\t\tpart="password-toggle" \n\t\t\ttype="button" \n\t\t\thidden \n\t\t\taria-label="Toggle password visibility"\n\t\t\taria-pressed="false"\n\t\t>\n\t\t\t<svg class="icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n\t\t\t\t<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>\n\t\t\t\t<circle cx="12" cy="12" r="3"></circle>\n\t\t\t</svg>\n\t\t\t\n\t\t\t<svg class="icon-eye-off" hidden viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n\t\t\t\t<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>\n\t\t\t\t<line x1="1" y1="1" x2="23" y2="23"></line>\n\t\t\t</svg>\n\t\t</button>\n\n\t\t\x3c!-- suffix slot here --\x3e\n\t</div>\n`;
702
709
 
703
710
  var FcInput = class extends HTMLElement {
704
711
  constructor() {
@@ -712,6 +719,16 @@ var FcInput = class extends HTMLElement {
712
719
  });
713
720
  shadow.appendChild(template3.content.cloneNode(true));
714
721
  this.internals = this.attachInternals();
722
+ this.inputEl = this.shadowRoot.querySelector(".fc-input-field");
723
+ this.passwordBtnEl = this.shadowRoot.querySelector(".fc-password-toggle");
724
+ this.fcPassEnableIcon = this.shadowRoot.querySelector(".icon-eye");
725
+ this.fcPassDisableIcon = this.shadowRoot.querySelector(".icon-eye-off");
726
+ const randomId = Math.random().toString(36).substring(2, 9);
727
+ const inputId = `fc-input-${randomId}`;
728
+ const passwordBtnId = `fc-pass-btn-${randomId}`;
729
+ this.inputEl.id = inputId;
730
+ this.passwordBtnEl.id = passwordBtnId;
731
+ this.passwordBtnEl.setAttribute("aria-controls", inputId);
715
732
  this.onInput = this.onInput.bind(this);
716
733
  this.onChange = this.onChange.bind(this);
717
734
  this.onBlur = this.onBlur.bind(this);
@@ -857,10 +874,9 @@ var FcInput = class extends HTMLElement {
857
874
  this.setAttribute("pattern", val);
858
875
  }
859
876
  connectedCallback() {
860
- this.inputEl = this.shadowRoot.getElementById("fc-field");
861
- this.passwordBtnEl = this.shadowRoot.getElementById("btn-show-pass");
862
- this.fcPassEnableIcon = this.shadowRoot.querySelector(".icon-eye");
863
- this.fcPassDisableIcon = this.shadowRoot.querySelector(".icon-eye-off");
877
+ if (this.type !== "file") {
878
+ this.internals.setFormValue(this._value);
879
+ }
864
880
  if (this.hasAttribute("type")) {
865
881
  const type = this.getAttribute("type");
866
882
  this.ALLOWED_TYPES.includes(type) ? this.inputEl.type = type : this.inputEl.type = "text";
@@ -907,9 +923,6 @@ var FcInput = class extends HTMLElement {
907
923
  if (this.type !== "file") {
908
924
  this.inputEl.value = this._value;
909
925
  }
910
- if (this.type !== "file") {
911
- this.internals.setFormValue(this._value);
912
- }
913
926
  this.inputEl.addEventListener("input", this.onInput);
914
927
  this.inputEl.addEventListener("change", this.onChange);
915
928
  this.inputEl.addEventListener("blur", this.onBlur);
@@ -1138,7 +1151,7 @@ var defineInput = () => {
1138
1151
  return FcInput;
1139
1152
  };
1140
1153
 
1141
- var styles4 = `\n :host {\n display: block;\n width: 100%;\n\t\tbox-sizing: border-box;\n contain: content;\n max-width: var(--fc-error-max-width);\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .fc-error-text {\n color: var(--fc-error-color);\n font-family: var(--fc-font-family, inherit);\n font-size: var(--fc-error-font-size);\n display: flex;\n align-items: center;\n gap: 4px;\n }\n`;
1154
+ var styles4 = `\n :host {\n display: block;\n width: 100%;\n\t\tbox-sizing: border-box;\n contain: content;\n max-width: var(--fc-error-max-width);\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .fc-error-text {\n color: var(--fc-error-fg);\n font-family: var(--fc-font-family, inherit);\n font-size: var(--fc-error-font-size);\n display: flex;\n align-items: center;\n gap: 4px;\n }\n`;
1142
1155
 
1143
1156
  var template4 = document.createElement("template");
1144
1157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fancy-ui-ts",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "A library to easily create cool and customizable webcomponents.",
6
6
  "main": "dist/index.js",