@duskmoon-dev/el-base 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -0
- package/dist/cjs/index.js +990 -0
- package/dist/cjs/index.js.map +16 -0
- package/dist/esm/index.js +958 -0
- package/dist/esm/index.js.map +16 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/animations.d.ts +53 -0
- package/dist/types/animations.d.ts.map +1 -0
- package/dist/types/base-element.d.ts +148 -0
- package/dist/types/base-element.d.ts.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/mixins.d.ts +1494 -0
- package/dist/types/mixins.d.ts.map +1 -0
- package/dist/types/performance.d.ts +49 -0
- package/dist/types/performance.d.ts.map +1 -0
- package/dist/types/styles.d.ts +64 -0
- package/dist/types/styles.d.ts.map +1 -0
- package/dist/types/themes.d.ts +55 -0
- package/dist/types/themes.d.ts.map +1 -0
- package/dist/types/types.d.ts +83 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/validation.d.ts +64 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# @duskmoon-dev/el-base
|
|
2
|
+
|
|
3
|
+
Core utilities and base classes for DuskMoon custom elements.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @duskmoon-dev/el-base
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### BaseElement
|
|
14
|
+
|
|
15
|
+
The `BaseElement` class provides a foundation for creating custom elements with:
|
|
16
|
+
|
|
17
|
+
- Shadow DOM setup with `adoptedStyleSheets`
|
|
18
|
+
- Reactive properties with attribute reflection
|
|
19
|
+
- Batched updates using microtask queue
|
|
20
|
+
- Style injection utilities
|
|
21
|
+
- Event emission helpers
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { BaseElement, css } from '@duskmoon-dev/el-base';
|
|
25
|
+
|
|
26
|
+
const styles = css`
|
|
27
|
+
:host {
|
|
28
|
+
display: block;
|
|
29
|
+
}
|
|
30
|
+
.greeting {
|
|
31
|
+
color: var(--dm-primary);
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
class MyGreeting extends BaseElement {
|
|
36
|
+
static properties = {
|
|
37
|
+
name: { type: String, reflect: true, default: 'World' },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
declare name: string;
|
|
41
|
+
|
|
42
|
+
constructor() {
|
|
43
|
+
super();
|
|
44
|
+
this.attachStyles(styles);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
render() {
|
|
48
|
+
return `<div class="greeting">Hello, ${this.name}!</div>`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
customElements.define('my-greeting', MyGreeting);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### CSS Utilities
|
|
56
|
+
|
|
57
|
+
#### `css` Template Tag
|
|
58
|
+
|
|
59
|
+
Creates a `CSSStyleSheet` from a template literal:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { css } from '@duskmoon-dev/el-base';
|
|
63
|
+
|
|
64
|
+
const styles = css`
|
|
65
|
+
:host {
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
}
|
|
68
|
+
button {
|
|
69
|
+
padding: 0.5rem 1rem;
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### `combineStyles`
|
|
75
|
+
|
|
76
|
+
Combines multiple stylesheets:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { combineStyles } from '@duskmoon-dev/el-base';
|
|
80
|
+
|
|
81
|
+
const combinedStyles = combineStyles(baseStyles, themeStyles, componentStyles);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### `cssVars`
|
|
85
|
+
|
|
86
|
+
Creates CSS custom property declarations:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { cssVars } from '@duskmoon-dev/el-base';
|
|
90
|
+
|
|
91
|
+
const vars = cssVars({
|
|
92
|
+
'dm-primary': '#3b82f6',
|
|
93
|
+
'dm-spacing': '1rem',
|
|
94
|
+
});
|
|
95
|
+
// Returns: '--dm-primary: #3b82f6; --dm-spacing: 1rem;'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Default Theme
|
|
99
|
+
|
|
100
|
+
The package includes default CSS custom properties for theming. Import the theme stylesheet:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { defaultTheme, resetStyles } from '@duskmoon-dev/el-base';
|
|
104
|
+
|
|
105
|
+
// Apply to shadow root
|
|
106
|
+
this.shadowRoot.adoptedStyleSheets = [resetStyles, defaultTheme, componentStyles];
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Color Tokens
|
|
110
|
+
|
|
111
|
+
| Variable | Description |
|
|
112
|
+
| ---------------- | ---------------------- |
|
|
113
|
+
| `--dm-primary` | Primary brand color |
|
|
114
|
+
| `--dm-secondary` | Secondary color |
|
|
115
|
+
| `--dm-success` | Success/positive color |
|
|
116
|
+
| `--dm-warning` | Warning color |
|
|
117
|
+
| `--dm-error` | Error/danger color |
|
|
118
|
+
| `--dm-info` | Information color |
|
|
119
|
+
|
|
120
|
+
#### Gray Scale
|
|
121
|
+
|
|
122
|
+
| Variable | Description |
|
|
123
|
+
| --------------------------------------- | ------------- |
|
|
124
|
+
| `--dm-gray-50` | Lightest gray |
|
|
125
|
+
| `--dm-gray-100` through `--dm-gray-800` | Gray scale |
|
|
126
|
+
| `--dm-gray-900` | Darkest gray |
|
|
127
|
+
|
|
128
|
+
#### Typography
|
|
129
|
+
|
|
130
|
+
| Variable | Description |
|
|
131
|
+
| ---------------------------------------------------------------------------------------------------------- | ---------------- |
|
|
132
|
+
| `--dm-font-family` | Base font family |
|
|
133
|
+
| `--dm-font-size-xs` through `--dm-font-size-2xl` | Font sizes |
|
|
134
|
+
| `--dm-font-weight-normal`, `--dm-font-weight-medium`, `--dm-font-weight-semibold`, `--dm-font-weight-bold` | Font weights |
|
|
135
|
+
| `--dm-line-height-tight`, `--dm-line-height-normal`, `--dm-line-height-relaxed` | Line heights |
|
|
136
|
+
|
|
137
|
+
#### Spacing
|
|
138
|
+
|
|
139
|
+
| Variable | Description |
|
|
140
|
+
| ----------------- | --------------------- |
|
|
141
|
+
| `--dm-spacing-xs` | Extra small (0.25rem) |
|
|
142
|
+
| `--dm-spacing-sm` | Small (0.5rem) |
|
|
143
|
+
| `--dm-spacing-md` | Medium (1rem) |
|
|
144
|
+
| `--dm-spacing-lg` | Large (1.5rem) |
|
|
145
|
+
| `--dm-spacing-xl` | Extra large (2rem) |
|
|
146
|
+
|
|
147
|
+
#### Border Radius
|
|
148
|
+
|
|
149
|
+
| Variable | Description |
|
|
150
|
+
| ------------------ | ---------------- |
|
|
151
|
+
| `--dm-radius-sm` | Small radius |
|
|
152
|
+
| `--dm-radius-md` | Medium radius |
|
|
153
|
+
| `--dm-radius-lg` | Large radius |
|
|
154
|
+
| `--dm-radius-full` | Full/pill radius |
|
|
155
|
+
|
|
156
|
+
#### Shadows
|
|
157
|
+
|
|
158
|
+
| Variable | Description |
|
|
159
|
+
| ---------------- | ------------- |
|
|
160
|
+
| `--dm-shadow-sm` | Small shadow |
|
|
161
|
+
| `--dm-shadow-md` | Medium shadow |
|
|
162
|
+
| `--dm-shadow-lg` | Large shadow |
|
|
163
|
+
|
|
164
|
+
#### Transitions
|
|
165
|
+
|
|
166
|
+
| Variable | Description |
|
|
167
|
+
| ------------------------ | ------------------------- |
|
|
168
|
+
| `--dm-transition-fast` | Fast transition (150ms) |
|
|
169
|
+
| `--dm-transition-normal` | Normal transition (200ms) |
|
|
170
|
+
| `--dm-transition-slow` | Slow transition (300ms) |
|
|
171
|
+
|
|
172
|
+
## API
|
|
173
|
+
|
|
174
|
+
### BaseElement
|
|
175
|
+
|
|
176
|
+
| Method | Description |
|
|
177
|
+
| ---------------------- | ------------------------------------------------ |
|
|
178
|
+
| `attachStyles(styles)` | Attach one or more stylesheets to Shadow DOM |
|
|
179
|
+
| `render()` | Override to return HTML content string |
|
|
180
|
+
| `update()` | Called when reactive properties change (batched) |
|
|
181
|
+
| `emit(name, detail?)` | Emit a CustomEvent from the element |
|
|
182
|
+
| `query(selector)` | Query single element in Shadow DOM |
|
|
183
|
+
| `queryAll(selector)` | Query all matching elements in Shadow DOM |
|
|
184
|
+
|
|
185
|
+
### Property Definitions
|
|
186
|
+
|
|
187
|
+
Define reactive properties with automatic attribute reflection:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
static properties = {
|
|
191
|
+
// Simple string property
|
|
192
|
+
label: { type: String },
|
|
193
|
+
|
|
194
|
+
// Boolean with attribute reflection
|
|
195
|
+
disabled: { type: Boolean, reflect: true },
|
|
196
|
+
|
|
197
|
+
// Number with default value
|
|
198
|
+
count: { type: Number, default: 0 },
|
|
199
|
+
|
|
200
|
+
// Custom attribute name (kebab-case)
|
|
201
|
+
maxItems: { type: Number, attribute: 'max-items' },
|
|
202
|
+
|
|
203
|
+
// Object/Array (not reflected to attributes)
|
|
204
|
+
data: { type: Object },
|
|
205
|
+
items: { type: Array, default: [] },
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Property definition options:
|
|
210
|
+
|
|
211
|
+
| Option | Type | Description |
|
|
212
|
+
| ----------- | ---------- | ------------------------------------------------------------------ |
|
|
213
|
+
| `type` | `Function` | Type constructor: `String`, `Number`, `Boolean`, `Object`, `Array` |
|
|
214
|
+
| `reflect` | `boolean` | Whether to reflect property to attribute |
|
|
215
|
+
| `attribute` | `string` | Custom attribute name (defaults to lowercase property name) |
|
|
216
|
+
| `default` | `any` | Default value for the property |
|
|
217
|
+
|
|
218
|
+
### Lifecycle
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
class MyElement extends BaseElement {
|
|
222
|
+
// Called when element is added to DOM
|
|
223
|
+
connectedCallback() {
|
|
224
|
+
super.connectedCallback();
|
|
225
|
+
// Setup code
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Called when element is removed from DOM
|
|
229
|
+
disconnectedCallback() {
|
|
230
|
+
super.disconnectedCallback();
|
|
231
|
+
// Cleanup code
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Called when properties change (batched)
|
|
235
|
+
update() {
|
|
236
|
+
// Re-render or update DOM
|
|
237
|
+
this.shadowRoot.innerHTML = this.render();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Return HTML string for Shadow DOM content
|
|
241
|
+
render() {
|
|
242
|
+
return `<div>Content</div>`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### TypeScript Types
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import type {
|
|
251
|
+
PropertyDefinition,
|
|
252
|
+
PropertyDefinitions,
|
|
253
|
+
Size,
|
|
254
|
+
Variant,
|
|
255
|
+
ValidationState,
|
|
256
|
+
BaseElementProps,
|
|
257
|
+
SizableProps,
|
|
258
|
+
VariantProps,
|
|
259
|
+
FormElementProps,
|
|
260
|
+
ValidatableProps,
|
|
261
|
+
ValueChangeEventDetail,
|
|
262
|
+
} from '@duskmoon-dev/el-base';
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Creating Custom Elements
|
|
266
|
+
|
|
267
|
+
Full example of a custom element:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { BaseElement, css, defaultTheme, resetStyles } from '@duskmoon-dev/el-base';
|
|
271
|
+
|
|
272
|
+
const styles = css`
|
|
273
|
+
:host {
|
|
274
|
+
display: inline-block;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.counter {
|
|
278
|
+
display: flex;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: var(--dm-spacing-sm);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
button {
|
|
284
|
+
padding: var(--dm-spacing-xs) var(--dm-spacing-sm);
|
|
285
|
+
border-radius: var(--dm-radius-sm);
|
|
286
|
+
background: var(--dm-primary);
|
|
287
|
+
color: white;
|
|
288
|
+
border: none;
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
transition: opacity var(--dm-transition-fast);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
button:hover {
|
|
294
|
+
opacity: 0.9;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.value {
|
|
298
|
+
min-width: 2rem;
|
|
299
|
+
text-align: center;
|
|
300
|
+
font-weight: var(--dm-font-weight-medium);
|
|
301
|
+
}
|
|
302
|
+
`;
|
|
303
|
+
|
|
304
|
+
export class MyCounter extends BaseElement {
|
|
305
|
+
static properties = {
|
|
306
|
+
value: { type: Number, reflect: true, default: 0 },
|
|
307
|
+
min: { type: Number, default: 0 },
|
|
308
|
+
max: { type: Number, default: 100 },
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
declare value: number;
|
|
312
|
+
declare min: number;
|
|
313
|
+
declare max: number;
|
|
314
|
+
|
|
315
|
+
constructor() {
|
|
316
|
+
super();
|
|
317
|
+
this.attachStyles(resetStyles, defaultTheme, styles);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
connectedCallback() {
|
|
321
|
+
super.connectedCallback();
|
|
322
|
+
this.shadowRoot?.addEventListener('click', this.handleClick.bind(this));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private handleClick(e: Event) {
|
|
326
|
+
const target = e.target as HTMLElement;
|
|
327
|
+
if (target.matches('[data-action="decrement"]')) {
|
|
328
|
+
this.decrement();
|
|
329
|
+
} else if (target.matches('[data-action="increment"]')) {
|
|
330
|
+
this.increment();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private decrement() {
|
|
335
|
+
if (this.value > this.min) {
|
|
336
|
+
this.value--;
|
|
337
|
+
this.emit('change', { value: this.value });
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private increment() {
|
|
342
|
+
if (this.value < this.max) {
|
|
343
|
+
this.value++;
|
|
344
|
+
this.emit('change', { value: this.value });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
render() {
|
|
349
|
+
return `
|
|
350
|
+
<div class="counter">
|
|
351
|
+
<button data-action="decrement" ${this.value <= this.min ? 'disabled' : ''}>-</button>
|
|
352
|
+
<span class="value">${this.value}</span>
|
|
353
|
+
<button data-action="increment" ${this.value >= this.max ? 'disabled' : ''}>+</button>
|
|
354
|
+
</div>
|
|
355
|
+
`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function register() {
|
|
360
|
+
if (!customElements.get('my-counter')) {
|
|
361
|
+
customElements.define('my-counter', MyCounter);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## License
|
|
367
|
+
|
|
368
|
+
MIT
|