@duskmoon-dev/el-card 0.1.0 → 0.3.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 +26 -3
- package/dist/cjs/index.js +59 -130
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/register.js +199 -0
- package/dist/cjs/register.js.map +12 -0
- package/dist/esm/index.js +59 -130
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/register.js +163 -0
- package/dist/esm/register.js.map +12 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-card.d.ts +5 -6
- package/dist/types/el-dm-card.d.ts.map +1 -1
- package/dist/types/register.d.ts +2 -0
- package/dist/types/register.d.ts.map +1 -0
- package/package.json +20 -5
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// src/el-dm-card.ts
|
|
2
2
|
import { BaseElement, css } from "@duskmoon-dev/el-core";
|
|
3
|
+
import { css as cardCSS } from "@duskmoon-dev/core/components/card";
|
|
4
|
+
var VARIANT_CLASSES = {
|
|
5
|
+
elevated: "card-elevated",
|
|
6
|
+
outlined: "card-bordered",
|
|
7
|
+
filled: ""
|
|
8
|
+
};
|
|
9
|
+
var coreStyles = cardCSS.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
3
10
|
var styles = css`
|
|
4
11
|
:host {
|
|
5
12
|
display: block;
|
|
@@ -9,155 +16,56 @@ var styles = css`
|
|
|
9
16
|
display: none !important;
|
|
10
17
|
}
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
flex-direction: column;
|
|
15
|
-
border-radius: var(--dm-card-border-radius, var(--dm-radius-lg, 0.75rem));
|
|
16
|
-
background-color: var(--dm-card-background, white);
|
|
17
|
-
overflow: hidden;
|
|
18
|
-
transition:
|
|
19
|
-
box-shadow var(--dm-transition-normal, 200ms ease),
|
|
20
|
-
transform var(--dm-transition-normal, 200ms ease);
|
|
21
|
-
}
|
|
19
|
+
/* Import core card styles */
|
|
20
|
+
${coreStyles}
|
|
22
21
|
|
|
23
|
-
/*
|
|
24
|
-
:host(:not([variant])) .card,
|
|
25
|
-
:host([variant='elevated']) .card {
|
|
26
|
-
box-shadow: var(--dm-card-shadow, var(--dm-shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1)));
|
|
27
|
-
}
|
|
22
|
+
/* Web component specific adjustments */
|
|
28
23
|
|
|
29
|
-
/*
|
|
30
|
-
|
|
31
|
-
border: 1px solid var(--dm-card-border-color, var(--dm-gray-200, #e5e7eb));
|
|
32
|
-
box-shadow: none;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/* Filled variant */
|
|
36
|
-
:host([variant='filled']) .card {
|
|
37
|
-
background-color: var(--dm-card-background, var(--dm-gray-50, #f9fafb));
|
|
38
|
-
box-shadow: none;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* Interactive styles */
|
|
42
|
-
:host([interactive]) .card {
|
|
43
|
-
cursor: pointer;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
:host([interactive]) .card:hover {
|
|
47
|
-
transform: translateY(-2px);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
:host([interactive][variant='elevated']) .card:hover,
|
|
51
|
-
:host([interactive]:not([variant])) .card:hover {
|
|
52
|
-
box-shadow: var(--dm-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
:host([interactive][variant='outlined']) .card:hover {
|
|
56
|
-
border-color: var(--dm-primary, #3b82f6);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
:host([interactive][variant='filled']) .card:hover {
|
|
60
|
-
background-color: var(--dm-gray-100, #f3f4f6);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
:host([interactive]) .card:focus-visible {
|
|
64
|
-
outline: none;
|
|
65
|
-
box-shadow: var(--dm-focus-ring-offset, 0 0 0 2px white, 0 0 0 4px var(--dm-primary, #3b82f6));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/* Media slot */
|
|
69
|
-
.media {
|
|
24
|
+
/* Media slot handling */
|
|
25
|
+
.card-image {
|
|
70
26
|
display: none;
|
|
71
27
|
}
|
|
72
28
|
|
|
73
|
-
.
|
|
29
|
+
.card-image.has-content {
|
|
74
30
|
display: block;
|
|
75
31
|
}
|
|
76
32
|
|
|
77
|
-
.
|
|
33
|
+
.card-image ::slotted(*) {
|
|
78
34
|
display: block;
|
|
79
35
|
width: 100%;
|
|
80
36
|
object-fit: cover;
|
|
81
37
|
}
|
|
82
38
|
|
|
83
|
-
/* Header */
|
|
84
|
-
.header {
|
|
39
|
+
/* Header slot handling */
|
|
40
|
+
.card-header {
|
|
85
41
|
display: none;
|
|
86
|
-
padding: var(--dm-card-padding, var(--dm-spacing-md, 1rem));
|
|
87
|
-
padding-bottom: 0;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.header.has-content {
|
|
91
|
-
display: block;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/* Body */
|
|
95
|
-
.body {
|
|
96
|
-
flex: 1;
|
|
97
42
|
}
|
|
98
43
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
:host([padding='md']) .body {
|
|
102
|
-
padding: var(--dm-card-padding, var(--dm-spacing-md, 1rem));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
:host([padding='none']) .body {
|
|
106
|
-
padding: 0;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
:host([padding='sm']) .body {
|
|
110
|
-
padding: var(--dm-card-padding, var(--dm-spacing-sm, 0.5rem));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
:host([padding='lg']) .body {
|
|
114
|
-
padding: var(--dm-card-padding, var(--dm-spacing-lg, 1.5rem));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
:host(:not([padding])) .header,
|
|
118
|
-
:host([padding='md']) .header {
|
|
119
|
-
padding: var(--dm-card-padding, var(--dm-spacing-md, 1rem));
|
|
120
|
-
padding-bottom: 0;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
:host([padding='sm']) .header {
|
|
124
|
-
padding: var(--dm-card-padding, var(--dm-spacing-sm, 0.5rem));
|
|
125
|
-
padding-bottom: 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
:host([padding='lg']) .header {
|
|
129
|
-
padding: var(--dm-card-padding, var(--dm-spacing-lg, 1.5rem));
|
|
130
|
-
padding-bottom: 0;
|
|
44
|
+
.card-header.has-content {
|
|
45
|
+
display: flex;
|
|
131
46
|
}
|
|
132
47
|
|
|
133
|
-
/* Footer */
|
|
134
|
-
.footer {
|
|
48
|
+
/* Footer slot handling */
|
|
49
|
+
.card-footer {
|
|
135
50
|
display: none;
|
|
136
|
-
padding: var(--dm-card-padding, var(--dm-spacing-md, 1rem));
|
|
137
|
-
padding-top: 0;
|
|
138
|
-
border-top: 1px solid var(--dm-gray-100, #f3f4f6);
|
|
139
|
-
margin-top: var(--dm-spacing-md, 1rem);
|
|
140
51
|
}
|
|
141
52
|
|
|
142
|
-
.footer.has-content {
|
|
143
|
-
display:
|
|
53
|
+
.card-footer.has-content {
|
|
54
|
+
display: flex;
|
|
144
55
|
}
|
|
145
56
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
padding
|
|
149
|
-
margin-top: var(--dm-spacing-sm, 0.5rem);
|
|
57
|
+
/* Padding variants */
|
|
58
|
+
:host([padding='none']) .card-body {
|
|
59
|
+
padding: 0;
|
|
150
60
|
}
|
|
151
61
|
|
|
152
|
-
:host([padding='lg']) .
|
|
153
|
-
|
|
154
|
-
padding-top: 0;
|
|
155
|
-
margin-top: var(--dm-spacing-lg, 1.5rem);
|
|
62
|
+
:host([padding='lg']) .card-body {
|
|
63
|
+
--card-p: 2rem;
|
|
156
64
|
}
|
|
157
65
|
|
|
158
|
-
:host([padding='
|
|
159
|
-
|
|
160
|
-
|
|
66
|
+
:host([padding='lg']) .card-header,
|
|
67
|
+
:host([padding='lg']) .card-footer {
|
|
68
|
+
--card-p: 2rem;
|
|
161
69
|
}
|
|
162
70
|
`;
|
|
163
71
|
|
|
@@ -180,8 +88,15 @@ class ElDmCard extends BaseElement {
|
|
|
180
88
|
}
|
|
181
89
|
_handleSlotChange(event) {
|
|
182
90
|
const slot = event.target;
|
|
183
|
-
const slotName = slot.name
|
|
184
|
-
const
|
|
91
|
+
const slotName = slot.name;
|
|
92
|
+
const wrapperMap = {
|
|
93
|
+
media: "card-image",
|
|
94
|
+
header: "card-header",
|
|
95
|
+
footer: "card-footer",
|
|
96
|
+
"": "card-body"
|
|
97
|
+
};
|
|
98
|
+
const wrapperClass = wrapperMap[slotName] || "card-body";
|
|
99
|
+
const wrapper = this.shadowRoot.querySelector(`.${wrapperClass}`);
|
|
185
100
|
if (wrapper) {
|
|
186
101
|
const hasContent = slot.assignedNodes().length > 0;
|
|
187
102
|
wrapper.classList.toggle("has-content", hasContent);
|
|
@@ -200,19 +115,33 @@ class ElDmCard extends BaseElement {
|
|
|
200
115
|
});
|
|
201
116
|
}
|
|
202
117
|
}
|
|
118
|
+
_getCardClasses() {
|
|
119
|
+
const classes = ["card"];
|
|
120
|
+
if (this.variant && VARIANT_CLASSES[this.variant]) {
|
|
121
|
+
classes.push(VARIANT_CLASSES[this.variant]);
|
|
122
|
+
}
|
|
123
|
+
if (this.interactive) {
|
|
124
|
+
classes.push("card-interactive");
|
|
125
|
+
}
|
|
126
|
+
if (this.padding === "sm") {
|
|
127
|
+
classes.push("card-compact");
|
|
128
|
+
}
|
|
129
|
+
return classes.filter(Boolean).join(" ");
|
|
130
|
+
}
|
|
203
131
|
render() {
|
|
132
|
+
const cardClasses = this._getCardClasses();
|
|
204
133
|
return `
|
|
205
|
-
<div class="
|
|
206
|
-
<div class="
|
|
134
|
+
<div class="${cardClasses}" part="card">
|
|
135
|
+
<div class="card-image" part="media">
|
|
207
136
|
<slot name="media"></slot>
|
|
208
137
|
</div>
|
|
209
|
-
<div class="header" part="header">
|
|
138
|
+
<div class="card-header" part="header">
|
|
210
139
|
<slot name="header"></slot>
|
|
211
140
|
</div>
|
|
212
|
-
<div class="body" part="body">
|
|
141
|
+
<div class="card-body" part="body">
|
|
213
142
|
<slot></slot>
|
|
214
143
|
</div>
|
|
215
|
-
<div class="footer" part="footer">
|
|
144
|
+
<div class="card-footer" part="footer">
|
|
216
145
|
<slot name="footer"></slot>
|
|
217
146
|
</div>
|
|
218
147
|
</div>
|
|
@@ -231,5 +160,5 @@ export {
|
|
|
231
160
|
ElDmCard
|
|
232
161
|
};
|
|
233
162
|
|
|
234
|
-
//# debugId=
|
|
163
|
+
//# debugId=81B703C5BA43CDB864756E2164756E21
|
|
235
164
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/el-dm-card.ts", "../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * DuskMoon Card Element\n *\n * A container component with header, body, and footer sections.\n *\n * @element el-dm-card\n *\n * @attr {string} variant - Card variant: elevated, outlined, filled\n * @attr {boolean} interactive - Whether the card is clickable/hoverable\n * @attr {string} padding - Padding size: none, sm, md, lg\n *\n * @slot - Default slot for card body content\n * @slot header - Card header content\n * @slot footer - Card footer content\n * @slot media - Media content (image/video) at the top of card\n *\n * @csspart card - The main card container\n * @csspart header - The header section\n * @csspart body - The body section\n * @csspart footer - The footer section\n * @csspart media - The media section\n *\n * @fires click - Fired when interactive card is clicked\n
|
|
5
|
+
"/**\n * DuskMoon Card Element\n *\n * A container component with header, body, and footer sections.\n * Uses styles from @duskmoon-dev/core for consistent theming.\n *\n * @element el-dm-card\n *\n * @attr {string} variant - Card variant: elevated, outlined, filled\n * @attr {boolean} interactive - Whether the card is clickable/hoverable\n * @attr {string} padding - Padding size: none, sm, md, lg\n *\n * @slot - Default slot for card body content\n * @slot header - Card header content\n * @slot footer - Card footer content\n * @slot media - Media content (image/video) at the top of card\n *\n * @csspart card - The main card container\n * @csspart header - The header section\n * @csspart body - The body section\n * @csspart footer - The footer section\n * @csspart media - The media section\n *\n * @fires click - Fired when interactive card is clicked\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\nimport { css as cardCSS } from '@duskmoon-dev/core/components/card';\n\n/**\n * Card variant options\n */\nexport type CardVariant = 'elevated' | 'outlined' | 'filled';\n\n/**\n * Card padding options\n */\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\n\n// Map of variant attribute values to CSS classes\nconst VARIANT_CLASSES: Record<string, string> = {\n elevated: 'card-elevated',\n outlined: 'card-bordered',\n filled: '', // default card style\n};\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = cardCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Import core card styles */\n ${coreStyles}\n\n /* Web component specific adjustments */\n\n /* Media slot handling */\n .card-image {\n display: none;\n }\n\n .card-image.has-content {\n display: block;\n }\n\n .card-image ::slotted(*) {\n display: block;\n width: 100%;\n object-fit: cover;\n }\n\n /* Header slot handling */\n .card-header {\n display: none;\n }\n\n .card-header.has-content {\n display: flex;\n }\n\n /* Footer slot handling */\n .card-footer {\n display: none;\n }\n\n .card-footer.has-content {\n display: flex;\n }\n\n /* Padding variants */\n :host([padding='none']) .card-body {\n padding: 0;\n }\n\n :host([padding='lg']) .card-body {\n --card-p: 2rem;\n }\n\n :host([padding='lg']) .card-header,\n :host([padding='lg']) .card-footer {\n --card-p: 2rem;\n }\n`;\n\nexport class ElDmCard extends BaseElement {\n static properties = {\n variant: { type: String, reflect: true },\n interactive: { type: Boolean, reflect: true },\n padding: { type: String, reflect: true },\n };\n\n /** Card variant */\n declare variant: CardVariant;\n\n /** Whether the card is interactive */\n declare interactive: boolean;\n\n /** Card padding size */\n declare padding: CardPadding;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n\n // Set up slot change listeners for conditional display\n this.shadowRoot.addEventListener('slotchange', this._handleSlotChange.bind(this));\n\n // Set up click handler for interactive cards\n if (this.interactive) {\n this._setupInteractive();\n }\n }\n\n /**\n * Handle slot content changes\n */\n private _handleSlotChange(event: Event): void {\n const slot = event.target as HTMLSlotElement;\n const slotName = slot.name;\n\n // Map slot names to wrapper class names\n const wrapperMap: Record<string, string> = {\n media: 'card-image',\n header: 'card-header',\n footer: 'card-footer',\n '': 'card-body',\n };\n\n const wrapperClass = wrapperMap[slotName] || 'card-body';\n const wrapper = this.shadowRoot.querySelector(`.${wrapperClass}`);\n\n if (wrapper) {\n const hasContent = slot.assignedNodes().length > 0;\n wrapper.classList.toggle('has-content', hasContent);\n }\n }\n\n /**\n * Set up interactive card behavior\n */\n private _setupInteractive(): void {\n const card = this.shadowRoot.querySelector('.card') as HTMLElement;\n if (card) {\n card.setAttribute('tabindex', '0');\n card.setAttribute('role', 'button');\n\n card.addEventListener('keydown', (event: KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.emit('click');\n }\n });\n }\n }\n\n /**\n * Build CSS class string for the card\n */\n private _getCardClasses(): string {\n const classes = ['card'];\n\n // Add variant class\n if (this.variant && VARIANT_CLASSES[this.variant]) {\n classes.push(VARIANT_CLASSES[this.variant]);\n }\n\n // Add interactive class\n if (this.interactive) {\n classes.push('card-interactive');\n }\n\n // Add compact class for small padding\n if (this.padding === 'sm') {\n classes.push('card-compact');\n }\n\n return classes.filter(Boolean).join(' ');\n }\n\n render(): string {\n const cardClasses = this._getCardClasses();\n\n return `\n <div class=\"${cardClasses}\" part=\"card\">\n <div class=\"card-image\" part=\"media\">\n <slot name=\"media\"></slot>\n </div>\n <div class=\"card-header\" part=\"header\">\n <slot name=\"header\"></slot>\n </div>\n <div class=\"card-body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"card-footer\" part=\"footer\">\n <slot name=\"footer\"></slot>\n </div>\n </div>\n `;\n }\n}\n",
|
|
6
6
|
"/**\n * @duskmoon-dev/el-card\n *\n * DuskMoon Card custom element\n */\n\nimport { ElDmCard } from './el-dm-card.js';\n\nexport { ElDmCard };\nexport type { CardVariant, CardPadding } from './el-dm-card.js';\n\n/**\n * Register the el-dm-card custom element\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-card';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-card')) {\n customElements.define('el-dm-card', ElDmCard);\n }\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "81B703C5BA43CDB864756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/el-dm-card.ts
|
|
2
|
+
import { BaseElement, css } from "@duskmoon-dev/el-core";
|
|
3
|
+
import { css as cardCSS } from "@duskmoon-dev/core/components/card";
|
|
4
|
+
var VARIANT_CLASSES = {
|
|
5
|
+
elevated: "card-elevated",
|
|
6
|
+
outlined: "card-bordered",
|
|
7
|
+
filled: ""
|
|
8
|
+
};
|
|
9
|
+
var coreStyles = cardCSS.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
10
|
+
var styles = css`
|
|
11
|
+
:host {
|
|
12
|
+
display: block;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:host([hidden]) {
|
|
16
|
+
display: none !important;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Import core card styles */
|
|
20
|
+
${coreStyles}
|
|
21
|
+
|
|
22
|
+
/* Web component specific adjustments */
|
|
23
|
+
|
|
24
|
+
/* Media slot handling */
|
|
25
|
+
.card-image {
|
|
26
|
+
display: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.card-image.has-content {
|
|
30
|
+
display: block;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.card-image ::slotted(*) {
|
|
34
|
+
display: block;
|
|
35
|
+
width: 100%;
|
|
36
|
+
object-fit: cover;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Header slot handling */
|
|
40
|
+
.card-header {
|
|
41
|
+
display: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.card-header.has-content {
|
|
45
|
+
display: flex;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Footer slot handling */
|
|
49
|
+
.card-footer {
|
|
50
|
+
display: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.card-footer.has-content {
|
|
54
|
+
display: flex;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Padding variants */
|
|
58
|
+
:host([padding='none']) .card-body {
|
|
59
|
+
padding: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:host([padding='lg']) .card-body {
|
|
63
|
+
--card-p: 2rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:host([padding='lg']) .card-header,
|
|
67
|
+
:host([padding='lg']) .card-footer {
|
|
68
|
+
--card-p: 2rem;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
class ElDmCard extends BaseElement {
|
|
73
|
+
static properties = {
|
|
74
|
+
variant: { type: String, reflect: true },
|
|
75
|
+
interactive: { type: Boolean, reflect: true },
|
|
76
|
+
padding: { type: String, reflect: true }
|
|
77
|
+
};
|
|
78
|
+
constructor() {
|
|
79
|
+
super();
|
|
80
|
+
this.attachStyles(styles);
|
|
81
|
+
}
|
|
82
|
+
connectedCallback() {
|
|
83
|
+
super.connectedCallback();
|
|
84
|
+
this.shadowRoot.addEventListener("slotchange", this._handleSlotChange.bind(this));
|
|
85
|
+
if (this.interactive) {
|
|
86
|
+
this._setupInteractive();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
_handleSlotChange(event) {
|
|
90
|
+
const slot = event.target;
|
|
91
|
+
const slotName = slot.name;
|
|
92
|
+
const wrapperMap = {
|
|
93
|
+
media: "card-image",
|
|
94
|
+
header: "card-header",
|
|
95
|
+
footer: "card-footer",
|
|
96
|
+
"": "card-body"
|
|
97
|
+
};
|
|
98
|
+
const wrapperClass = wrapperMap[slotName] || "card-body";
|
|
99
|
+
const wrapper = this.shadowRoot.querySelector(`.${wrapperClass}`);
|
|
100
|
+
if (wrapper) {
|
|
101
|
+
const hasContent = slot.assignedNodes().length > 0;
|
|
102
|
+
wrapper.classList.toggle("has-content", hasContent);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
_setupInteractive() {
|
|
106
|
+
const card = this.shadowRoot.querySelector(".card");
|
|
107
|
+
if (card) {
|
|
108
|
+
card.setAttribute("tabindex", "0");
|
|
109
|
+
card.setAttribute("role", "button");
|
|
110
|
+
card.addEventListener("keydown", (event) => {
|
|
111
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
this.emit("click");
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
_getCardClasses() {
|
|
119
|
+
const classes = ["card"];
|
|
120
|
+
if (this.variant && VARIANT_CLASSES[this.variant]) {
|
|
121
|
+
classes.push(VARIANT_CLASSES[this.variant]);
|
|
122
|
+
}
|
|
123
|
+
if (this.interactive) {
|
|
124
|
+
classes.push("card-interactive");
|
|
125
|
+
}
|
|
126
|
+
if (this.padding === "sm") {
|
|
127
|
+
classes.push("card-compact");
|
|
128
|
+
}
|
|
129
|
+
return classes.filter(Boolean).join(" ");
|
|
130
|
+
}
|
|
131
|
+
render() {
|
|
132
|
+
const cardClasses = this._getCardClasses();
|
|
133
|
+
return `
|
|
134
|
+
<div class="${cardClasses}" part="card">
|
|
135
|
+
<div class="card-image" part="media">
|
|
136
|
+
<slot name="media"></slot>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="card-header" part="header">
|
|
139
|
+
<slot name="header"></slot>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="card-body" part="body">
|
|
142
|
+
<slot></slot>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="card-footer" part="footer">
|
|
145
|
+
<slot name="footer"></slot>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/index.ts
|
|
153
|
+
function register() {
|
|
154
|
+
if (!customElements.get("el-dm-card")) {
|
|
155
|
+
customElements.define("el-dm-card", ElDmCard);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/register.ts
|
|
160
|
+
register();
|
|
161
|
+
|
|
162
|
+
//# debugId=B63517D15474517F64756E2164756E21
|
|
163
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/el-dm-card.ts", "../../src/index.ts", "../../src/register.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * DuskMoon Card Element\n *\n * A container component with header, body, and footer sections.\n * Uses styles from @duskmoon-dev/core for consistent theming.\n *\n * @element el-dm-card\n *\n * @attr {string} variant - Card variant: elevated, outlined, filled\n * @attr {boolean} interactive - Whether the card is clickable/hoverable\n * @attr {string} padding - Padding size: none, sm, md, lg\n *\n * @slot - Default slot for card body content\n * @slot header - Card header content\n * @slot footer - Card footer content\n * @slot media - Media content (image/video) at the top of card\n *\n * @csspart card - The main card container\n * @csspart header - The header section\n * @csspart body - The body section\n * @csspart footer - The footer section\n * @csspart media - The media section\n *\n * @fires click - Fired when interactive card is clicked\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\nimport { css as cardCSS } from '@duskmoon-dev/core/components/card';\n\n/**\n * Card variant options\n */\nexport type CardVariant = 'elevated' | 'outlined' | 'filled';\n\n/**\n * Card padding options\n */\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\n\n// Map of variant attribute values to CSS classes\nconst VARIANT_CLASSES: Record<string, string> = {\n elevated: 'card-elevated',\n outlined: 'card-bordered',\n filled: '', // default card style\n};\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = cardCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Import core card styles */\n ${coreStyles}\n\n /* Web component specific adjustments */\n\n /* Media slot handling */\n .card-image {\n display: none;\n }\n\n .card-image.has-content {\n display: block;\n }\n\n .card-image ::slotted(*) {\n display: block;\n width: 100%;\n object-fit: cover;\n }\n\n /* Header slot handling */\n .card-header {\n display: none;\n }\n\n .card-header.has-content {\n display: flex;\n }\n\n /* Footer slot handling */\n .card-footer {\n display: none;\n }\n\n .card-footer.has-content {\n display: flex;\n }\n\n /* Padding variants */\n :host([padding='none']) .card-body {\n padding: 0;\n }\n\n :host([padding='lg']) .card-body {\n --card-p: 2rem;\n }\n\n :host([padding='lg']) .card-header,\n :host([padding='lg']) .card-footer {\n --card-p: 2rem;\n }\n`;\n\nexport class ElDmCard extends BaseElement {\n static properties = {\n variant: { type: String, reflect: true },\n interactive: { type: Boolean, reflect: true },\n padding: { type: String, reflect: true },\n };\n\n /** Card variant */\n declare variant: CardVariant;\n\n /** Whether the card is interactive */\n declare interactive: boolean;\n\n /** Card padding size */\n declare padding: CardPadding;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n\n // Set up slot change listeners for conditional display\n this.shadowRoot.addEventListener('slotchange', this._handleSlotChange.bind(this));\n\n // Set up click handler for interactive cards\n if (this.interactive) {\n this._setupInteractive();\n }\n }\n\n /**\n * Handle slot content changes\n */\n private _handleSlotChange(event: Event): void {\n const slot = event.target as HTMLSlotElement;\n const slotName = slot.name;\n\n // Map slot names to wrapper class names\n const wrapperMap: Record<string, string> = {\n media: 'card-image',\n header: 'card-header',\n footer: 'card-footer',\n '': 'card-body',\n };\n\n const wrapperClass = wrapperMap[slotName] || 'card-body';\n const wrapper = this.shadowRoot.querySelector(`.${wrapperClass}`);\n\n if (wrapper) {\n const hasContent = slot.assignedNodes().length > 0;\n wrapper.classList.toggle('has-content', hasContent);\n }\n }\n\n /**\n * Set up interactive card behavior\n */\n private _setupInteractive(): void {\n const card = this.shadowRoot.querySelector('.card') as HTMLElement;\n if (card) {\n card.setAttribute('tabindex', '0');\n card.setAttribute('role', 'button');\n\n card.addEventListener('keydown', (event: KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.emit('click');\n }\n });\n }\n }\n\n /**\n * Build CSS class string for the card\n */\n private _getCardClasses(): string {\n const classes = ['card'];\n\n // Add variant class\n if (this.variant && VARIANT_CLASSES[this.variant]) {\n classes.push(VARIANT_CLASSES[this.variant]);\n }\n\n // Add interactive class\n if (this.interactive) {\n classes.push('card-interactive');\n }\n\n // Add compact class for small padding\n if (this.padding === 'sm') {\n classes.push('card-compact');\n }\n\n return classes.filter(Boolean).join(' ');\n }\n\n render(): string {\n const cardClasses = this._getCardClasses();\n\n return `\n <div class=\"${cardClasses}\" part=\"card\">\n <div class=\"card-image\" part=\"media\">\n <slot name=\"media\"></slot>\n </div>\n <div class=\"card-header\" part=\"header\">\n <slot name=\"header\"></slot>\n </div>\n <div class=\"card-body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"card-footer\" part=\"footer\">\n <slot name=\"footer\"></slot>\n </div>\n </div>\n `;\n }\n}\n",
|
|
6
|
+
"/**\n * @duskmoon-dev/el-card\n *\n * DuskMoon Card custom element\n */\n\nimport { ElDmCard } from './el-dm-card.js';\n\nexport { ElDmCard };\nexport type { CardVariant, CardPadding } from './el-dm-card.js';\n\n/**\n * Register the el-dm-card custom element\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-card';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-card')) {\n customElements.define('el-dm-card', ElDmCard);\n }\n}\n",
|
|
7
|
+
"/**\n * Auto-register el-dm-card custom element\n *\n * @example\n * ```ts\n * import '@duskmoon-dev/el-card/register';\n *\n * // Now you can use <el-dm-card> in HTML\n * ```\n */\nimport { register } from './index.js';\n\nregister();\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "B63517D15474517F64756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|