@bquery/bquery 1.4.0 → 1.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.
- package/README.md +586 -527
- package/dist/component/component.d.ts +13 -5
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/html.d.ts +40 -3
- package/dist/component/html.d.ts.map +1 -1
- package/dist/component/index.d.ts +4 -2
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/library.d.ts +34 -0
- package/dist/component/library.d.ts.map +1 -0
- package/dist/component/types.d.ts +132 -13
- package/dist/component/types.d.ts.map +1 -1
- package/dist/component-BEQgt5hl.js +600 -0
- package/dist/component-BEQgt5hl.js.map +1 -0
- package/dist/component.es.mjs +7 -184
- package/dist/config-DRmZZno3.js +40 -0
- package/dist/config-DRmZZno3.js.map +1 -0
- package/dist/core-BGQJVw0-.js +35 -0
- package/dist/core-BGQJVw0-.js.map +1 -0
- package/dist/core-CCEabVHl.js +648 -0
- package/dist/core-CCEabVHl.js.map +1 -0
- package/dist/core.es.mjs +45 -1261
- package/dist/effect-AFRW_Plg.js +84 -0
- package/dist/effect-AFRW_Plg.js.map +1 -0
- package/dist/full.d.ts +8 -8
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +101 -91
- package/dist/full.iife.js +173 -3
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +173 -3
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +147 -139
- package/dist/motion/transition.d.ts +1 -1
- package/dist/motion/transition.d.ts.map +1 -1
- package/dist/motion/types.d.ts +11 -1
- package/dist/motion/types.d.ts.map +1 -1
- package/dist/motion-D9TcHxOF.js +415 -0
- package/dist/motion-D9TcHxOF.js.map +1 -0
- package/dist/motion.es.mjs +25 -361
- package/dist/object-qGpWr6-J.js +38 -0
- package/dist/object-qGpWr6-J.js.map +1 -0
- package/dist/platform/announcer.d.ts +59 -0
- package/dist/platform/announcer.d.ts.map +1 -0
- package/dist/platform/config.d.ts +92 -0
- package/dist/platform/config.d.ts.map +1 -0
- package/dist/platform/cookies.d.ts +45 -0
- package/dist/platform/cookies.d.ts.map +1 -0
- package/dist/platform/index.d.ts +8 -0
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/meta.d.ts +62 -0
- package/dist/platform/meta.d.ts.map +1 -0
- package/dist/platform-Dr9b6fsq.js +362 -0
- package/dist/platform-Dr9b6fsq.js.map +1 -0
- package/dist/platform.es.mjs +11 -248
- package/dist/reactive/async-data.d.ts +114 -0
- package/dist/reactive/async-data.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/signal.d.ts +2 -0
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive-DSkct0dO.js +254 -0
- package/dist/reactive-DSkct0dO.js.map +1 -0
- package/dist/reactive.es.mjs +18 -32
- package/dist/router-CbDhl8rS.js +188 -0
- package/dist/router-CbDhl8rS.js.map +1 -0
- package/dist/router.es.mjs +11 -200
- package/dist/sanitize-Bs2dkMby.js +313 -0
- package/dist/sanitize-Bs2dkMby.js.map +1 -0
- package/dist/security/constants.d.ts.map +1 -1
- package/dist/security/index.d.ts +4 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize.d.ts +4 -1
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-html.d.ts +53 -0
- package/dist/security/trusted-html.d.ts.map +1 -0
- package/dist/security.es.mjs +11 -56
- package/dist/store/define-store.d.ts +1 -1
- package/dist/store/define-store.d.ts.map +1 -1
- package/dist/store/mapping.d.ts +1 -1
- package/dist/store/mapping.d.ts.map +1 -1
- package/dist/store/persisted.d.ts +1 -1
- package/dist/store/persisted.d.ts.map +1 -1
- package/dist/store/types.d.ts +2 -2
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/watch.d.ts +1 -1
- package/dist/store/watch.d.ts.map +1 -1
- package/dist/store-BwDvI45q.js +263 -0
- package/dist/store-BwDvI45q.js.map +1 -0
- package/dist/store.es.mjs +12 -25
- package/dist/storybook/index.d.ts +37 -0
- package/dist/storybook/index.d.ts.map +1 -0
- package/dist/storybook.es.mjs +151 -0
- package/dist/storybook.es.mjs.map +1 -0
- package/dist/untrack-B0rVscTc.js +7 -0
- package/dist/untrack-B0rVscTc.js.map +1 -0
- package/dist/view-C70lA3vf.js +397 -0
- package/dist/view-C70lA3vf.js.map +1 -0
- package/dist/view.es.mjs +11 -430
- package/package.json +141 -132
- package/src/component/component.ts +524 -289
- package/src/component/html.ts +153 -53
- package/src/component/index.ts +50 -40
- package/src/component/library.ts +518 -0
- package/src/component/types.ts +256 -85
- package/src/core/collection.ts +628 -628
- package/src/core/element.ts +774 -774
- package/src/core/index.ts +48 -48
- package/src/core/utils/function.ts +151 -151
- package/src/full.ts +229 -187
- package/src/motion/animate.ts +113 -113
- package/src/motion/flip.ts +176 -176
- package/src/motion/scroll.ts +57 -57
- package/src/motion/spring.ts +150 -150
- package/src/motion/timeline.ts +246 -246
- package/src/motion/transition.ts +97 -51
- package/src/motion/types.ts +11 -1
- package/src/platform/announcer.ts +208 -0
- package/src/platform/config.ts +163 -0
- package/src/platform/cookies.ts +165 -0
- package/src/platform/index.ts +21 -0
- package/src/platform/meta.ts +168 -0
- package/src/platform/storage.ts +215 -215
- package/src/reactive/async-data.ts +486 -0
- package/src/reactive/core.ts +114 -114
- package/src/reactive/effect.ts +54 -54
- package/src/reactive/index.ts +15 -1
- package/src/reactive/internals.ts +122 -122
- package/src/reactive/signal.ts +9 -0
- package/src/security/constants.ts +3 -1
- package/src/security/index.ts +17 -10
- package/src/security/sanitize-core.ts +364 -364
- package/src/security/sanitize.ts +70 -66
- package/src/security/trusted-html.ts +71 -0
- package/src/store/define-store.ts +49 -48
- package/src/store/mapping.ts +74 -73
- package/src/store/persisted.ts +62 -61
- package/src/store/types.ts +92 -94
- package/src/store/watch.ts +53 -52
- package/src/storybook/index.ts +479 -0
- package/src/view/evaluate.ts +290 -290
- package/dist/batch-x7b2eZST.js +0 -13
- package/dist/batch-x7b2eZST.js.map +0 -1
- package/dist/component.es.mjs.map +0 -1
- package/dist/core-BhpuvPhy.js +0 -170
- package/dist/core-BhpuvPhy.js.map +0 -1
- package/dist/core.es.mjs.map +0 -1
- package/dist/full.es.mjs.map +0 -1
- package/dist/index.es.mjs.map +0 -1
- package/dist/motion.es.mjs.map +0 -1
- package/dist/persisted-DHoi3uEs.js +0 -278
- package/dist/persisted-DHoi3uEs.js.map +0 -1
- package/dist/platform.es.mjs.map +0 -1
- package/dist/reactive.es.mjs.map +0 -1
- package/dist/router.es.mjs.map +0 -1
- package/dist/sanitize-Cxvxa-DX.js +0 -283
- package/dist/sanitize-Cxvxa-DX.js.map +0 -1
- package/dist/security.es.mjs.map +0 -1
- package/dist/store.es.mjs.map +0 -1
- package/dist/type-guards-BdKlYYlS.js +0 -32
- package/dist/type-guards-BdKlYYlS.js.map +0 -1
- package/dist/untrack-DNnnqdlR.js +0 -6
- package/dist/untrack-DNnnqdlR.js.map +0 -1
- package/dist/view.es.mjs.map +0 -1
- package/dist/watch-DXXv3iAI.js +0 -58
- package/dist/watch-DXXv3iAI.js.map +0 -1
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default component library based on native Web Components.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/component
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getBqueryConfig } from '../platform/config';
|
|
8
|
+
import { escapeHtml } from '../security';
|
|
9
|
+
import { component } from './component';
|
|
10
|
+
import { html } from './html';
|
|
11
|
+
|
|
12
|
+
/** Options for registering the default component library. */
|
|
13
|
+
export interface DefaultComponentLibraryOptions {
|
|
14
|
+
/** Prefix used for all registered component tags. Defaults to `bq`. */
|
|
15
|
+
prefix?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Tag names returned by registerDefaultComponents(). */
|
|
19
|
+
export interface RegisteredDefaultComponents {
|
|
20
|
+
/** Button component tag name. */
|
|
21
|
+
button: string;
|
|
22
|
+
/** Card component tag name. */
|
|
23
|
+
card: string;
|
|
24
|
+
/** Input component tag name. */
|
|
25
|
+
input: string;
|
|
26
|
+
/** Textarea component tag name. */
|
|
27
|
+
textarea: string;
|
|
28
|
+
/** Checkbox component tag name. */
|
|
29
|
+
checkbox: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const baseStyles = `
|
|
33
|
+
:host {
|
|
34
|
+
color: inherit;
|
|
35
|
+
font: inherit;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const controlStyles = `
|
|
40
|
+
${baseStyles}
|
|
41
|
+
.field {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: 0.375rem;
|
|
45
|
+
width: 100%;
|
|
46
|
+
}
|
|
47
|
+
.label {
|
|
48
|
+
color: #334155;
|
|
49
|
+
font-size: 0.875rem;
|
|
50
|
+
font-weight: 600;
|
|
51
|
+
}
|
|
52
|
+
.control {
|
|
53
|
+
border: 1px solid #cbd5e1;
|
|
54
|
+
border-radius: 0.75rem;
|
|
55
|
+
box-sizing: border-box;
|
|
56
|
+
font: inherit;
|
|
57
|
+
min-height: 2.75rem;
|
|
58
|
+
outline: none;
|
|
59
|
+
padding: 0.75rem 0.875rem;
|
|
60
|
+
width: 100%;
|
|
61
|
+
background: #fff;
|
|
62
|
+
color: #0f172a;
|
|
63
|
+
transition: border-color 160ms ease, box-shadow 160ms ease;
|
|
64
|
+
}
|
|
65
|
+
.control:focus {
|
|
66
|
+
border-color: #2563eb;
|
|
67
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
|
|
68
|
+
}
|
|
69
|
+
.control:disabled {
|
|
70
|
+
background: #f8fafc;
|
|
71
|
+
color: #94a3b8;
|
|
72
|
+
cursor: not-allowed;
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const escapeProp = (value: string): string => escapeHtml(value);
|
|
77
|
+
|
|
78
|
+
const handlerStore = new WeakMap<HTMLElement, Record<string, EventListener>>();
|
|
79
|
+
|
|
80
|
+
const readHandler = (element: HTMLElement, key: string): EventListener | undefined => {
|
|
81
|
+
return handlerStore.get(element)?.[key];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const storeHandler = (element: HTMLElement, key: string, value: EventListener): void => {
|
|
85
|
+
const handlers = handlerStore.get(element) ?? {};
|
|
86
|
+
handlers[key] = value;
|
|
87
|
+
handlerStore.set(element, handlers);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Detect a value-only input update, patch the live control in place, and
|
|
92
|
+
* return whether the component can skip a full shadow DOM re-render.
|
|
93
|
+
*
|
|
94
|
+
* @param element - The host custom element whose shadow DOM is being updated
|
|
95
|
+
* @param newProps - The next reflected input props for the pending update
|
|
96
|
+
* @param oldProps - The previous reflected input props from the last render
|
|
97
|
+
*/
|
|
98
|
+
const canSkipInputRender = (
|
|
99
|
+
element: HTMLElement,
|
|
100
|
+
newProps: {
|
|
101
|
+
label: string;
|
|
102
|
+
type: string;
|
|
103
|
+
value: string;
|
|
104
|
+
placeholder: string;
|
|
105
|
+
name: string;
|
|
106
|
+
disabled: boolean;
|
|
107
|
+
},
|
|
108
|
+
oldProps: {
|
|
109
|
+
label: string;
|
|
110
|
+
type: string;
|
|
111
|
+
value: string;
|
|
112
|
+
placeholder: string;
|
|
113
|
+
name: string;
|
|
114
|
+
disabled: boolean;
|
|
115
|
+
}
|
|
116
|
+
): boolean => {
|
|
117
|
+
if (oldProps.label !== newProps.label) return false;
|
|
118
|
+
if (oldProps.type !== newProps.type) return false;
|
|
119
|
+
if (oldProps.placeholder !== newProps.placeholder) return false;
|
|
120
|
+
if (oldProps.name !== newProps.name) return false;
|
|
121
|
+
if (oldProps.disabled !== newProps.disabled) return false;
|
|
122
|
+
|
|
123
|
+
const control = element.shadowRoot?.querySelector('input.control') as HTMLInputElement | null;
|
|
124
|
+
if (!control) return false;
|
|
125
|
+
|
|
126
|
+
if (control.value !== newProps.value) {
|
|
127
|
+
control.value = newProps.value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return true;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Detect a value-only textarea update, patch the live control in place, and
|
|
135
|
+
* return whether the component can skip a full shadow DOM re-render.
|
|
136
|
+
*
|
|
137
|
+
* @param element - The host custom element whose shadow DOM is being updated
|
|
138
|
+
* @param newProps - The next reflected textarea props for the pending update
|
|
139
|
+
* @param oldProps - The previous reflected textarea props from the last render
|
|
140
|
+
*/
|
|
141
|
+
const canSkipTextareaRender = (
|
|
142
|
+
element: HTMLElement,
|
|
143
|
+
newProps: {
|
|
144
|
+
label: string;
|
|
145
|
+
value: string;
|
|
146
|
+
placeholder: string;
|
|
147
|
+
name: string;
|
|
148
|
+
rows: number;
|
|
149
|
+
disabled: boolean;
|
|
150
|
+
},
|
|
151
|
+
oldProps: {
|
|
152
|
+
label: string;
|
|
153
|
+
value: string;
|
|
154
|
+
placeholder: string;
|
|
155
|
+
name: string;
|
|
156
|
+
rows: number;
|
|
157
|
+
disabled: boolean;
|
|
158
|
+
}
|
|
159
|
+
): boolean => {
|
|
160
|
+
if (oldProps.label !== newProps.label) return false;
|
|
161
|
+
if (oldProps.placeholder !== newProps.placeholder) return false;
|
|
162
|
+
if (oldProps.name !== newProps.name) return false;
|
|
163
|
+
if (oldProps.rows !== newProps.rows) return false;
|
|
164
|
+
if (oldProps.disabled !== newProps.disabled) return false;
|
|
165
|
+
|
|
166
|
+
const control = element.shadowRoot?.querySelector(
|
|
167
|
+
'textarea.control'
|
|
168
|
+
) as HTMLTextAreaElement | null;
|
|
169
|
+
if (!control) return false;
|
|
170
|
+
|
|
171
|
+
if (control.value !== newProps.value) {
|
|
172
|
+
control.value = newProps.value;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const renderTextareaControl = (props: {
|
|
178
|
+
value: string;
|
|
179
|
+
placeholder: string;
|
|
180
|
+
name: string;
|
|
181
|
+
rows: number;
|
|
182
|
+
disabled: boolean;
|
|
183
|
+
}): string => {
|
|
184
|
+
return [
|
|
185
|
+
'<textarea',
|
|
186
|
+
' part="control"',
|
|
187
|
+
' class="control"',
|
|
188
|
+
` placeholder="${escapeProp(props.placeholder)}"`,
|
|
189
|
+
` name="${escapeProp(props.name)}"`,
|
|
190
|
+
` rows="${props.rows}"`,
|
|
191
|
+
props.disabled ? ' disabled' : '',
|
|
192
|
+
`>${escapeProp(props.value)}</textarea>`,
|
|
193
|
+
].join('');
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Register a default set of foundational UI components.
|
|
198
|
+
*
|
|
199
|
+
* The library is intentionally small and dependency-free, providing common
|
|
200
|
+
* primitives that can be themed via shadow parts and CSS custom properties.
|
|
201
|
+
*
|
|
202
|
+
* @param options - Optional registration settings such as a custom tag prefix
|
|
203
|
+
* @returns The registered tag names for each component
|
|
204
|
+
*/
|
|
205
|
+
export const registerDefaultComponents = (
|
|
206
|
+
options: DefaultComponentLibraryOptions = {}
|
|
207
|
+
): RegisteredDefaultComponents => {
|
|
208
|
+
const prefix = options.prefix ?? getBqueryConfig().components?.prefix ?? 'bq';
|
|
209
|
+
const tags: RegisteredDefaultComponents = {
|
|
210
|
+
button: `${prefix}-button`,
|
|
211
|
+
card: `${prefix}-card`,
|
|
212
|
+
input: `${prefix}-input`,
|
|
213
|
+
textarea: `${prefix}-textarea`,
|
|
214
|
+
checkbox: `${prefix}-checkbox`,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
component<{
|
|
218
|
+
label: string;
|
|
219
|
+
variant: string;
|
|
220
|
+
size: string;
|
|
221
|
+
type: string;
|
|
222
|
+
disabled: boolean;
|
|
223
|
+
}>(tags.button, {
|
|
224
|
+
props: {
|
|
225
|
+
label: { type: String, default: '' },
|
|
226
|
+
variant: { type: String, default: 'primary' },
|
|
227
|
+
size: { type: String, default: 'md' },
|
|
228
|
+
type: { type: String, default: 'button' },
|
|
229
|
+
disabled: { type: Boolean, default: false },
|
|
230
|
+
},
|
|
231
|
+
styles: `
|
|
232
|
+
${baseStyles}
|
|
233
|
+
button {
|
|
234
|
+
appearance: none;
|
|
235
|
+
border: 0;
|
|
236
|
+
border-radius: 999px;
|
|
237
|
+
cursor: pointer;
|
|
238
|
+
display: inline-flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
justify-content: center;
|
|
241
|
+
font: inherit;
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
gap: 0.5rem;
|
|
244
|
+
min-height: 2.5rem;
|
|
245
|
+
padding: 0.65rem 1rem;
|
|
246
|
+
transition: transform 160ms ease, opacity 160ms ease, background 160ms ease;
|
|
247
|
+
background: #2563eb;
|
|
248
|
+
color: #fff;
|
|
249
|
+
}
|
|
250
|
+
button[data-variant='secondary'] {
|
|
251
|
+
background: #e2e8f0;
|
|
252
|
+
color: #0f172a;
|
|
253
|
+
}
|
|
254
|
+
button[data-size='sm'] {
|
|
255
|
+
min-height: 2.125rem;
|
|
256
|
+
padding: 0.5rem 0.875rem;
|
|
257
|
+
}
|
|
258
|
+
button[data-size='lg'] {
|
|
259
|
+
min-height: 3rem;
|
|
260
|
+
padding: 0.875rem 1.25rem;
|
|
261
|
+
}
|
|
262
|
+
button:hover:not(:disabled) {
|
|
263
|
+
transform: translateY(-1px);
|
|
264
|
+
}
|
|
265
|
+
button:disabled {
|
|
266
|
+
cursor: not-allowed;
|
|
267
|
+
opacity: 0.6;
|
|
268
|
+
}
|
|
269
|
+
`,
|
|
270
|
+
render: ({ props }) => html`
|
|
271
|
+
<button
|
|
272
|
+
part="button"
|
|
273
|
+
type="${escapeProp(props.type)}"
|
|
274
|
+
data-variant="${escapeProp(props.variant)}"
|
|
275
|
+
data-size="${escapeProp(props.size)}"
|
|
276
|
+
${props.disabled ? 'disabled' : ''}
|
|
277
|
+
>
|
|
278
|
+
<slot>${escapeProp(props.label)}</slot>
|
|
279
|
+
</button>
|
|
280
|
+
`,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
component<{ title: string; footer: string; elevated: boolean }>(tags.card, {
|
|
284
|
+
props: {
|
|
285
|
+
title: { type: String, default: '' },
|
|
286
|
+
footer: { type: String, default: '' },
|
|
287
|
+
elevated: { type: Boolean, default: true },
|
|
288
|
+
},
|
|
289
|
+
styles: `
|
|
290
|
+
${baseStyles}
|
|
291
|
+
article {
|
|
292
|
+
background: #fff;
|
|
293
|
+
border: 1px solid #e2e8f0;
|
|
294
|
+
border-radius: 1rem;
|
|
295
|
+
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.08);
|
|
296
|
+
color: #0f172a;
|
|
297
|
+
display: block;
|
|
298
|
+
padding: 1rem;
|
|
299
|
+
}
|
|
300
|
+
article[data-elevated='false'] {
|
|
301
|
+
box-shadow: none;
|
|
302
|
+
}
|
|
303
|
+
header, footer {
|
|
304
|
+
color: #475569;
|
|
305
|
+
font-size: 0.95rem;
|
|
306
|
+
font-weight: 600;
|
|
307
|
+
}
|
|
308
|
+
header {
|
|
309
|
+
margin-bottom: 0.75rem;
|
|
310
|
+
}
|
|
311
|
+
footer {
|
|
312
|
+
margin-top: 0.75rem;
|
|
313
|
+
}
|
|
314
|
+
`,
|
|
315
|
+
render: ({ props }) => html`
|
|
316
|
+
<article part="card" data-elevated="${String(props.elevated)}">
|
|
317
|
+
${props.title ? `<header part="header">${escapeProp(props.title)}</header>` : ''}
|
|
318
|
+
<section part="body"><slot></slot></section>
|
|
319
|
+
${props.footer ? `<footer part="footer">${escapeProp(props.footer)}</footer>` : ''}
|
|
320
|
+
</article>
|
|
321
|
+
`,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
component<{
|
|
325
|
+
label: string;
|
|
326
|
+
type: string;
|
|
327
|
+
value: string;
|
|
328
|
+
placeholder: string;
|
|
329
|
+
name: string;
|
|
330
|
+
disabled: boolean;
|
|
331
|
+
}>(tags.input, {
|
|
332
|
+
props: {
|
|
333
|
+
label: { type: String, default: '' },
|
|
334
|
+
type: { type: String, default: 'text' },
|
|
335
|
+
value: { type: String, default: '' },
|
|
336
|
+
placeholder: { type: String, default: '' },
|
|
337
|
+
name: { type: String, default: '' },
|
|
338
|
+
disabled: { type: Boolean, default: false },
|
|
339
|
+
},
|
|
340
|
+
styles: controlStyles,
|
|
341
|
+
/**
|
|
342
|
+
* Skip the full shadow DOM re-render when only the reflected input value
|
|
343
|
+
* changed, because the live control has already been patched in place.
|
|
344
|
+
*/
|
|
345
|
+
beforeUpdate(newProps, oldProps) {
|
|
346
|
+
if (canSkipInputRender(this, newProps, oldProps)) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
return true;
|
|
350
|
+
},
|
|
351
|
+
connected() {
|
|
352
|
+
const handleInput = (event: Event) => {
|
|
353
|
+
const target = event.target as HTMLInputElement | null;
|
|
354
|
+
if (!target?.matches('input')) return;
|
|
355
|
+
event.stopPropagation();
|
|
356
|
+
this.setAttribute('value', target.value);
|
|
357
|
+
this.dispatchEvent(
|
|
358
|
+
new CustomEvent('input', {
|
|
359
|
+
detail: { value: target.value },
|
|
360
|
+
bubbles: true,
|
|
361
|
+
composed: true,
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
storeHandler(this, '__bqueryInputHandler', handleInput);
|
|
366
|
+
this.shadowRoot?.addEventListener('input', handleInput);
|
|
367
|
+
},
|
|
368
|
+
disconnected() {
|
|
369
|
+
const handleInput = readHandler(this, '__bqueryInputHandler');
|
|
370
|
+
if (handleInput) {
|
|
371
|
+
this.shadowRoot?.removeEventListener('input', handleInput);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
render: ({ props }) => html`
|
|
375
|
+
<label part="field" class="field">
|
|
376
|
+
${props.label ? `<span part="label" class="label">${escapeProp(props.label)}</span>` : ''}
|
|
377
|
+
<input
|
|
378
|
+
part="control"
|
|
379
|
+
class="control"
|
|
380
|
+
type="${escapeProp(props.type)}"
|
|
381
|
+
value="${escapeProp(props.value)}"
|
|
382
|
+
placeholder="${escapeProp(props.placeholder)}"
|
|
383
|
+
name="${escapeProp(props.name)}"
|
|
384
|
+
${props.disabled ? 'disabled' : ''}
|
|
385
|
+
/>
|
|
386
|
+
</label>
|
|
387
|
+
`,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
component<{
|
|
391
|
+
label: string;
|
|
392
|
+
value: string;
|
|
393
|
+
placeholder: string;
|
|
394
|
+
name: string;
|
|
395
|
+
rows: number;
|
|
396
|
+
disabled: boolean;
|
|
397
|
+
}>(tags.textarea, {
|
|
398
|
+
props: {
|
|
399
|
+
label: { type: String, default: '' },
|
|
400
|
+
value: { type: String, default: '' },
|
|
401
|
+
placeholder: { type: String, default: '' },
|
|
402
|
+
name: { type: String, default: '' },
|
|
403
|
+
rows: { type: Number, default: 4 },
|
|
404
|
+
disabled: { type: Boolean, default: false },
|
|
405
|
+
},
|
|
406
|
+
styles: `${controlStyles}
|
|
407
|
+
textarea.control {
|
|
408
|
+
min-height: 6rem;
|
|
409
|
+
resize: vertical;
|
|
410
|
+
}
|
|
411
|
+
`,
|
|
412
|
+
/**
|
|
413
|
+
* Skip the full shadow DOM re-render when only the reflected textarea value
|
|
414
|
+
* changed, because the live control has already been patched in place.
|
|
415
|
+
*/
|
|
416
|
+
beforeUpdate(newProps, oldProps) {
|
|
417
|
+
if (canSkipTextareaRender(this, newProps, oldProps)) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
return true;
|
|
421
|
+
},
|
|
422
|
+
connected() {
|
|
423
|
+
const handleInput = (event: Event) => {
|
|
424
|
+
const target = event.target as HTMLTextAreaElement | null;
|
|
425
|
+
if (!target?.matches('textarea')) return;
|
|
426
|
+
event.stopPropagation();
|
|
427
|
+
this.setAttribute('value', target.value);
|
|
428
|
+
this.dispatchEvent(
|
|
429
|
+
new CustomEvent('input', {
|
|
430
|
+
detail: { value: target.value },
|
|
431
|
+
bubbles: true,
|
|
432
|
+
composed: true,
|
|
433
|
+
})
|
|
434
|
+
);
|
|
435
|
+
};
|
|
436
|
+
storeHandler(this, '__bqueryTextareaHandler', handleInput);
|
|
437
|
+
this.shadowRoot?.addEventListener('input', handleInput);
|
|
438
|
+
},
|
|
439
|
+
disconnected() {
|
|
440
|
+
const handleInput = readHandler(this, '__bqueryTextareaHandler');
|
|
441
|
+
if (handleInput) {
|
|
442
|
+
this.shadowRoot?.removeEventListener('input', handleInput);
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
render: ({ props }) => html`
|
|
446
|
+
<label part="field" class="field">
|
|
447
|
+
${props.label ? `<span part="label" class="label">${escapeProp(props.label)}</span>` : ''}
|
|
448
|
+
${renderTextareaControl(props)}
|
|
449
|
+
</label>
|
|
450
|
+
`,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
component<{ label: string; checked: boolean; disabled: boolean }>(tags.checkbox, {
|
|
454
|
+
props: {
|
|
455
|
+
label: { type: String, default: '' },
|
|
456
|
+
checked: { type: Boolean, default: false },
|
|
457
|
+
disabled: { type: Boolean, default: false },
|
|
458
|
+
},
|
|
459
|
+
styles: `
|
|
460
|
+
${baseStyles}
|
|
461
|
+
label {
|
|
462
|
+
align-items: center;
|
|
463
|
+
color: #0f172a;
|
|
464
|
+
cursor: pointer;
|
|
465
|
+
display: inline-flex;
|
|
466
|
+
gap: 0.625rem;
|
|
467
|
+
}
|
|
468
|
+
input {
|
|
469
|
+
accent-color: #2563eb;
|
|
470
|
+
block-size: 1rem;
|
|
471
|
+
inline-size: 1rem;
|
|
472
|
+
}
|
|
473
|
+
input:disabled {
|
|
474
|
+
cursor: not-allowed;
|
|
475
|
+
}
|
|
476
|
+
`,
|
|
477
|
+
connected() {
|
|
478
|
+
const handleChange = (event: Event) => {
|
|
479
|
+
const target = event.target as HTMLInputElement | null;
|
|
480
|
+
if (!target?.matches('input[type="checkbox"]')) return;
|
|
481
|
+
event.stopPropagation();
|
|
482
|
+
if (target.checked) {
|
|
483
|
+
this.setAttribute('checked', 'true');
|
|
484
|
+
} else {
|
|
485
|
+
this.removeAttribute('checked');
|
|
486
|
+
}
|
|
487
|
+
this.dispatchEvent(
|
|
488
|
+
new CustomEvent('change', {
|
|
489
|
+
detail: { checked: target.checked },
|
|
490
|
+
bubbles: true,
|
|
491
|
+
composed: true,
|
|
492
|
+
})
|
|
493
|
+
);
|
|
494
|
+
};
|
|
495
|
+
storeHandler(this, '__bqueryCheckboxHandler', handleChange);
|
|
496
|
+
this.shadowRoot?.addEventListener('change', handleChange);
|
|
497
|
+
},
|
|
498
|
+
disconnected() {
|
|
499
|
+
const handleChange = readHandler(this, '__bqueryCheckboxHandler');
|
|
500
|
+
if (handleChange) {
|
|
501
|
+
this.shadowRoot?.removeEventListener('change', handleChange);
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
render: ({ props }) => html`
|
|
505
|
+
<label part="label">
|
|
506
|
+
<input
|
|
507
|
+
part="control"
|
|
508
|
+
type="checkbox"
|
|
509
|
+
${props.checked ? 'checked' : ''}
|
|
510
|
+
${props.disabled ? 'disabled' : ''}
|
|
511
|
+
/>
|
|
512
|
+
<span part="text"><slot>${escapeProp(props.label)}</slot></span>
|
|
513
|
+
</label>
|
|
514
|
+
`,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
return tags;
|
|
518
|
+
};
|