lightview 1.8.2 → 2.0.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/.agent/workflows/daisyui-component-migration.md +155 -0
- package/.codacy/cli.sh +149 -0
- package/.codacy/codacy.yaml +15 -0
- package/.github/instructions/codacy.instructions.md +72 -0
- package/.wranglerignore +21 -0
- package/README.md +1330 -19
- package/_headers +4 -0
- package/build.js +70 -0
- package/components/actions/button.js +151 -0
- package/components/actions/dropdown.js +120 -0
- package/components/actions/modal.js +146 -0
- package/components/actions/swap.js +118 -0
- package/components/daisyui.js +288 -0
- package/components/data-display/accordion.js +128 -0
- package/components/data-display/alert.js +112 -0
- package/components/data-display/avatar.js +170 -0
- package/components/data-display/badge.js +82 -0
- package/components/data-display/card.js +151 -0
- package/components/data-display/carousel.js +94 -0
- package/components/data-display/chart.js +220 -0
- package/components/data-display/chat.js +128 -0
- package/components/data-display/collapse.js +103 -0
- package/components/data-display/countdown.js +69 -0
- package/components/data-display/diff.js +111 -0
- package/components/data-display/kbd.js +65 -0
- package/components/data-display/loading.js +75 -0
- package/components/data-display/progress.js +79 -0
- package/components/data-display/radial-progress.js +88 -0
- package/components/data-display/skeleton.js +66 -0
- package/components/data-display/stats.js +159 -0
- package/components/data-display/table.js +146 -0
- package/components/data-display/timeline.js +146 -0
- package/components/data-display/toast.js +72 -0
- package/components/data-display/tooltip.js +74 -0
- package/components/data-input/checkbox.js +253 -0
- package/components/data-input/file-input.js +224 -0
- package/components/data-input/input.js +264 -0
- package/components/data-input/radio.js +338 -0
- package/components/data-input/range.js +204 -0
- package/components/data-input/rating.js +219 -0
- package/components/data-input/select.js +287 -0
- package/components/data-input/textarea.js +287 -0
- package/components/data-input/toggle.js +201 -0
- package/components/index.js +137 -0
- package/components/layout/divider.js +72 -0
- package/components/layout/drawer.js +142 -0
- package/components/layout/footer.js +100 -0
- package/components/layout/hero.js +109 -0
- package/components/layout/indicator.js +90 -0
- package/components/layout/join.js +78 -0
- package/components/layout/navbar.js +110 -0
- package/components/navigation/breadcrumbs.js +91 -0
- package/components/navigation/dock.js +103 -0
- package/components/navigation/menu.js +126 -0
- package/components/navigation/pagination.js +105 -0
- package/components/navigation/steps.js +89 -0
- package/components/navigation/tabs.css +177 -0
- package/components/navigation/tabs.js +123 -0
- package/components/theme/theme-switch.css +65 -0
- package/components/theme/theme-switch.js +177 -0
- package/docs/about.html +164 -0
- package/docs/api/computed.html +184 -0
- package/docs/api/effects.html +173 -0
- package/docs/api/elements.html +180 -0
- package/docs/api/enhance.html +225 -0
- package/docs/api/hypermedia.html +165 -0
- package/docs/api/index.html +178 -0
- package/docs/api/nav.html +18 -0
- package/docs/api/signals.html +136 -0
- package/docs/api/state.html +217 -0
- package/docs/assets/images/logo-favicon.svg +42 -0
- package/docs/assets/images/logo-static.svg +40 -0
- package/docs/assets/images/logo.svg +66 -0
- package/docs/assets/js/examplify.js +395 -0
- package/docs/assets/styles/site.css +1102 -0
- package/docs/assets/styles/themes.css +236 -0
- package/docs/components/accordion.html +439 -0
- package/docs/components/alert.html +528 -0
- package/docs/components/avatar.html +586 -0
- package/docs/components/badge.html +531 -0
- package/docs/components/breadcrumbs.html +278 -0
- package/docs/components/button.html +579 -0
- package/docs/components/card.html +561 -0
- package/docs/components/carousel.html +286 -0
- package/docs/components/chart-area.html +702 -0
- package/docs/components/chart-bar.html +782 -0
- package/docs/components/chart-column.html +735 -0
- package/docs/components/chart-line.html +794 -0
- package/docs/components/chart-pie.html +823 -0
- package/docs/components/chart.html +610 -15
- package/docs/components/chat.html +547 -0
- package/docs/components/checkbox.html +641 -0
- package/docs/components/collapse.html +536 -0
- package/docs/components/component-nav.html +53 -0
- package/docs/components/countdown.html +470 -0
- package/docs/components/diff.html +245 -0
- package/docs/components/divider.html +240 -0
- package/docs/components/dock.html +277 -0
- package/docs/components/drawer.html +515 -0
- package/docs/components/dropdown.html +479 -0
- package/docs/components/file-input.html +591 -0
- package/docs/components/footer.html +301 -0
- package/docs/components/gallery.html +504 -0
- package/docs/components/hero.html +264 -0
- package/docs/components/index.css +840 -0
- package/docs/components/index.html +735 -0
- package/docs/components/indicator.html +342 -0
- package/docs/components/input.html +644 -0
- package/docs/components/join.html +285 -0
- package/docs/components/kbd.html +322 -0
- package/docs/components/loading.html +521 -0
- package/docs/components/menu.html +461 -0
- package/docs/components/modal.html +639 -0
- package/docs/components/navbar.html +321 -0
- package/docs/components/pagination.html +279 -0
- package/docs/components/progress.html +514 -0
- package/docs/components/radial-progress.html +434 -0
- package/docs/components/radio.html +655 -0
- package/docs/components/range.html +611 -0
- package/docs/components/rating.html +642 -0
- package/docs/components/select.html +696 -0
- package/docs/components/sidebar-setup.js +93 -0
- package/docs/components/skeleton.html +447 -0
- package/docs/components/spinner.html +68 -0
- package/docs/components/stats.html +486 -0
- package/docs/components/steps.html +356 -0
- package/docs/components/swap.html +517 -0
- package/docs/components/switch.html +68 -0
- package/docs/components/table.html +668 -0
- package/docs/components/tabs.html +506 -0
- package/docs/components/text-input.html +68 -0
- package/docs/components/textarea.html +603 -0
- package/docs/components/timeline.html +485 -42
- package/docs/components/toast.html +474 -0
- package/docs/components/toggle.html +564 -0
- package/docs/components/tooltip.html +423 -0
- package/docs/examples/getting-started-example.html +40 -0
- package/docs/examples/index.html +93 -0
- package/docs/getting-started/index.html +739 -0
- package/docs/getting-started/reviews.html +23 -0
- package/docs/getting-started/reviews.odom +108 -0
- package/docs/getting-started/reviews.vdom +84 -0
- package/docs/index.html +132 -42
- package/docs/playground.html +416 -0
- package/docs/router.html +285 -0
- package/docs/styles/index.html +190 -0
- package/functions/_middleware.js +32 -0
- package/index.html +309 -0
- package/lightview-router.js +364 -0
- package/lightview-x.js +1577 -0
- package/lightview.js +659 -1200
- package/lightview.js.backup +793 -0
- package/middleware/locale.js +25 -0
- package/middleware/markdown.js +44 -0
- package/middleware/notFound.js +37 -0
- package/package.json +27 -41
- package/watch.js +92 -0
- package/wrangler.toml +12 -0
- package/.idea/lightview.iml +0 -12
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/LICENSE +0 -21
- package/codepen-no-tabs-embed.css +0 -2
- package/docs/CNAME +0 -1
- package/docs/api.html +0 -674
- package/docs/blank.html +0 -10
- package/docs/comparedto.html +0 -89
- package/docs/components/chart-repl.html +0 -69
- package/docs/components/components.js +0 -113
- package/docs/components/contents.html +0 -17
- package/docs/components/gantt-repl.html +0 -61
- package/docs/components/gantt.html +0 -42
- package/docs/components/gauge-repl.html +0 -66
- package/docs/components/gauge.html +0 -20
- package/docs/components/orgchart-repl.html +0 -64
- package/docs/components/orgchart.html +0 -41
- package/docs/components/repl-as-src.html +0 -17
- package/docs/components/repl-repl.html +0 -95
- package/docs/components/repl.html +0 -527
- package/docs/components/timeline-repl.html +0 -72
- package/docs/components.html +0 -14
- package/docs/css/highlightjs.min.css +0 -9
- package/docs/css/tutorial.css +0 -35
- package/docs/examples/anchor.html +0 -11
- package/docs/examples/chart.html +0 -34
- package/docs/examples/counter.html +0 -26
- package/docs/examples/counter.test.mjs +0 -47
- package/docs/examples/counter2.html +0 -26
- package/docs/examples/directives.html +0 -79
- package/docs/examples/foreign.html +0 -50
- package/docs/examples/forgeinform.html +0 -98
- package/docs/examples/form.html +0 -61
- package/docs/examples/gauge.html +0 -18
- package/docs/examples/invalid-template-literals.html +0 -44
- package/docs/examples/medium/remote.html +0 -60
- package/docs/examples/message.html +0 -18
- package/docs/examples/nested.html +0 -11
- package/docs/examples/object-bound-form.html +0 -34
- package/docs/examples/remote-server.js +0 -51
- package/docs/examples/remote.html +0 -34
- package/docs/examples/remote.json +0 -1
- package/docs/examples/scratch.html +0 -69
- package/docs/examples/sensors/index.html +0 -44
- package/docs/examples/sensors/sensor-server.js +0 -30
- package/docs/examples/shared.html +0 -41
- package/docs/examples/template.html +0 -33
- package/docs/examples/timeline.html +0 -21
- package/docs/examples/todo.html +0 -40
- package/docs/examples/top.html +0 -10
- package/docs/examples/types.html +0 -94
- package/docs/examples/xor.html +0 -62
- package/docs/examples.html +0 -25
- package/docs/javascript/codejar.min.js +0 -8
- package/docs/javascript/highlightjs.min.js +0 -1173
- package/docs/javascript/isomorphic-git.js +0 -9
- package/docs/javascript/json5.min.js +0 -1
- package/docs/javascript/lightning-fs.js +0 -1
- package/docs/javascript/lightview.js +0 -1285
- package/docs/javascript/marked.min.js +0 -6
- package/docs/javascript/peerjs.min.js +0 -70
- package/docs/javascript/turndown.js +0 -973
- package/docs/javascript/types.js +0 -606
- package/docs/javascript/utils.js +0 -45
- package/docs/lightview.html +0 -63
- package/docs/old_index.html +0 -965
- package/docs/old_index.md +0 -1132
- package/docs/slidein.html +0 -51
- package/docs/tutorial/0-getting-started.html +0 -67
- package/docs/tutorial/1-intro-to-variables.html +0 -103
- package/docs/tutorial/10-template-components.html +0 -80
- package/docs/tutorial/11-linked-components.html +0 -76
- package/docs/tutorial/12-imported-components.html +0 -67
- package/docs/tutorial/13-input-binding.html +0 -94
- package/docs/tutorial/14-automatic-variable-creation.html +0 -74
- package/docs/tutorial/15-form-binding.html +0 -110
- package/docs/tutorial/16-if-directive.html +0 -60
- package/docs/tutorial/17-loop-directives.html +0 -83
- package/docs/tutorial/18-sanitizing-and-escaping-input.html +0 -79
- package/docs/tutorial/2-imported-and-exported-variables.html +0 -80
- package/docs/tutorial/3-data-types.html +0 -89
- package/docs/tutorial/4-extended-data-types.html +0 -83
- package/docs/tutorial/5-extended-functional-types.html +0 -96
- package/docs/tutorial/5.1-extended-functional-types.html +0 -79
- package/docs/tutorial/5.2-extended-functional-types.html +0 -70
- package/docs/tutorial/6-conventional-javascript.html +0 -75
- package/docs/tutorial/7-monitoring-with-observers.html +0 -107
- package/docs/tutorial/8-event-listeners.html +0 -65
- package/docs/tutorial/9-intro-to-components.html +0 -91
- package/docs/tutorial/contents.html +0 -32
- package/docs/tutorial/my-component.html +0 -29
- package/docs/tutorial/remote-value.json +0 -4
- package/docs/websiterepl.html +0 -46
- package/jest-puppeteer.config.js +0 -5
- package/jest.config.json +0 -12
- package/lightview.min.js +0 -1
- package/lightview_good.js +0 -1267
- package/lightview_optimized.js +0 -1274
- package/repl_hold.html +0 -320
- package/test/basic.html +0 -104
- package/test/basic.test.mjs +0 -315
- package/test/extended.html +0 -29
- package/test/extended.test.mjs +0 -448
- package/types.js +0 -607
- package/unsplash.key +0 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightview Components - DaisyUI Integration
|
|
3
|
+
* This module ensures DaisyUI CSS is loaded and provides utilities for components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const DAISYUI_CDN = 'https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.min.css';
|
|
7
|
+
const TAILWIND_CDN = 'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4';
|
|
8
|
+
|
|
9
|
+
let daisyLoaded = false;
|
|
10
|
+
let tailwindLoaded = false;
|
|
11
|
+
|
|
12
|
+
// ============= ADOPTED STYLESHEETS SUPPORT =============
|
|
13
|
+
// Cached CSSStyleSheet objects for efficient shadow DOM usage
|
|
14
|
+
|
|
15
|
+
let daisyStyleSheet = null;
|
|
16
|
+
let daisyStyleSheetPromise = null;
|
|
17
|
+
const componentStyleSheets = new Map(); // Cache for component CSS
|
|
18
|
+
const componentStyleSheetPromises = new Map();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get a CSSStyleSheet for DaisyUI (for use with adoptedStyleSheets)
|
|
22
|
+
* Fetches and parses CSS once, caches for reuse
|
|
23
|
+
* @returns {Promise<CSSStyleSheet>}
|
|
24
|
+
*/
|
|
25
|
+
export const getDaisyStyleSheet = async () => {
|
|
26
|
+
// Return cached sheet if available
|
|
27
|
+
if (daisyStyleSheet) {
|
|
28
|
+
return daisyStyleSheet;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Return existing promise if fetch is in progress
|
|
32
|
+
if (daisyStyleSheetPromise) {
|
|
33
|
+
return daisyStyleSheetPromise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Fetch and create the stylesheet
|
|
37
|
+
daisyStyleSheetPromise = (async () => {
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(DAISYUI_CDN);
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Failed to fetch DaisyUI CSS: ${response.status}`);
|
|
42
|
+
}
|
|
43
|
+
const cssText = await response.text();
|
|
44
|
+
|
|
45
|
+
const sheet = new CSSStyleSheet();
|
|
46
|
+
sheet.replaceSync(cssText);
|
|
47
|
+
daisyStyleSheet = sheet;
|
|
48
|
+
return sheet;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error('Lightview: Failed to create DaisyUI adoptedStyleSheet:', e);
|
|
51
|
+
daisyStyleSheetPromise = null; // Allow retry
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
|
|
56
|
+
return daisyStyleSheetPromise;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get a CSSStyleSheet for a component's CSS file
|
|
61
|
+
* @param {string} cssUrl - URL to the component's CSS file
|
|
62
|
+
* @returns {Promise<CSSStyleSheet>}
|
|
63
|
+
*/
|
|
64
|
+
export const getComponentStyleSheet = async (cssUrl) => {
|
|
65
|
+
// Return cached sheet if available
|
|
66
|
+
if (componentStyleSheets.has(cssUrl)) {
|
|
67
|
+
return componentStyleSheets.get(cssUrl);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Return existing promise if fetch is in progress
|
|
71
|
+
if (componentStyleSheetPromises.has(cssUrl)) {
|
|
72
|
+
return componentStyleSheetPromises.get(cssUrl);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fetch and create the stylesheet
|
|
76
|
+
const promise = (async () => {
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(cssUrl);
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`Failed to fetch component CSS: ${response.status}`);
|
|
81
|
+
}
|
|
82
|
+
const cssText = await response.text();
|
|
83
|
+
|
|
84
|
+
const sheet = new CSSStyleSheet();
|
|
85
|
+
sheet.replaceSync(cssText);
|
|
86
|
+
componentStyleSheets.set(cssUrl, sheet);
|
|
87
|
+
return sheet;
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.error(`Lightview: Failed to create adoptedStyleSheet for ${cssUrl}:`, e);
|
|
90
|
+
componentStyleSheetPromises.delete(cssUrl); // Allow retry
|
|
91
|
+
throw e;
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
|
|
95
|
+
componentStyleSheetPromises.set(cssUrl, promise);
|
|
96
|
+
return promise;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Synchronously get cached DaisyUI stylesheet (returns null if not yet loaded)
|
|
101
|
+
* Use getDaisyStyleSheet() first to ensure it's loaded
|
|
102
|
+
* @returns {CSSStyleSheet|null}
|
|
103
|
+
*/
|
|
104
|
+
export const getDaisyStyleSheetSync = () => daisyStyleSheet;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Synchronously get cached component stylesheet (returns null if not yet loaded)
|
|
108
|
+
* @param {string} cssUrl
|
|
109
|
+
* @returns {CSSStyleSheet|null}
|
|
110
|
+
*/
|
|
111
|
+
export const getComponentStyleSheetSync = (cssUrl) => componentStyleSheets.get(cssUrl) || null;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pre-load all stylesheets needed for shadow DOM components
|
|
115
|
+
* Call this early in your app initialization for best performance
|
|
116
|
+
* @param {string[]} componentCssUrls - Optional array of component CSS URLs to preload
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
export const preloadShadowStyles = async (componentCssUrls = []) => {
|
|
120
|
+
const promises = [getDaisyStyleSheet()];
|
|
121
|
+
for (const url of componentCssUrls) {
|
|
122
|
+
promises.push(getComponentStyleSheet(url));
|
|
123
|
+
}
|
|
124
|
+
await Promise.all(promises);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Ensure DaisyUI CSS is loaded from CDN
|
|
129
|
+
*/
|
|
130
|
+
export const ensureDaisyUI = () => {
|
|
131
|
+
if (daisyLoaded) return Promise.resolve();
|
|
132
|
+
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
// Check if already loaded
|
|
135
|
+
if (document.querySelector('link[href*="daisyui"]')) {
|
|
136
|
+
daisyLoaded = true;
|
|
137
|
+
resolve();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const link = document.createElement('link');
|
|
142
|
+
link.rel = 'stylesheet';
|
|
143
|
+
link.href = DAISYUI_CDN;
|
|
144
|
+
link.id = 'daisyui-styles';
|
|
145
|
+
link.onload = () => {
|
|
146
|
+
daisyLoaded = true;
|
|
147
|
+
resolve();
|
|
148
|
+
};
|
|
149
|
+
link.onerror = () => {
|
|
150
|
+
console.warn('Failed to load DaisyUI from CDN');
|
|
151
|
+
resolve();
|
|
152
|
+
};
|
|
153
|
+
document.head.appendChild(link);
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Ensure Tailwind CSS Browser is loaded (for utility classes)
|
|
159
|
+
*/
|
|
160
|
+
export const ensureTailwind = () => {
|
|
161
|
+
if (tailwindLoaded) return Promise.resolve();
|
|
162
|
+
|
|
163
|
+
return new Promise((resolve) => {
|
|
164
|
+
// Check if already loaded
|
|
165
|
+
if (document.querySelector('script[src*="tailwindcss/browser"]')) {
|
|
166
|
+
tailwindLoaded = true;
|
|
167
|
+
resolve();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const script = document.createElement('script');
|
|
172
|
+
script.src = TAILWIND_CDN;
|
|
173
|
+
script.id = 'tailwind-browser';
|
|
174
|
+
script.onload = () => {
|
|
175
|
+
tailwindLoaded = true;
|
|
176
|
+
resolve();
|
|
177
|
+
};
|
|
178
|
+
script.onerror = () => {
|
|
179
|
+
console.warn('Failed to load Tailwind Browser from CDN');
|
|
180
|
+
resolve();
|
|
181
|
+
};
|
|
182
|
+
document.head.appendChild(script);
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Ensure all themes are loaded
|
|
188
|
+
*/
|
|
189
|
+
export const ensureThemes = () => {
|
|
190
|
+
return new Promise((resolve) => {
|
|
191
|
+
if (document.querySelector('link[href*="daisyui"][href*="themes"]')) {
|
|
192
|
+
resolve();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const link = document.createElement('link');
|
|
197
|
+
link.rel = 'stylesheet';
|
|
198
|
+
link.href = `${DAISYUI_CDN}/themes.css`;
|
|
199
|
+
link.id = 'daisyui-themes';
|
|
200
|
+
link.onload = () => resolve();
|
|
201
|
+
link.onerror = () => resolve();
|
|
202
|
+
document.head.appendChild(link);
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Initialize DaisyUI with optional Tailwind utilities
|
|
208
|
+
* @param {Object} options
|
|
209
|
+
* @param {boolean} options.tailwind - Whether to load Tailwind Browser (default: true)
|
|
210
|
+
* @param {boolean} options.themes - Whether to load all themes (default: false)
|
|
211
|
+
*/
|
|
212
|
+
export const init = async (options = {}) => {
|
|
213
|
+
const { tailwind = true, themes = false } = options;
|
|
214
|
+
|
|
215
|
+
const promises = [ensureDaisyUI()];
|
|
216
|
+
if (tailwind) promises.push(ensureTailwind());
|
|
217
|
+
if (themes) promises.push(ensureThemes());
|
|
218
|
+
|
|
219
|
+
await Promise.all(promises);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Set the current theme
|
|
224
|
+
* @param {string} theme - Theme name (e.g., 'light', 'dark', 'cupcake', 'cyberpunk')
|
|
225
|
+
*/
|
|
226
|
+
export const setTheme = (theme) => {
|
|
227
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get the current theme
|
|
232
|
+
* @returns {string}
|
|
233
|
+
*/
|
|
234
|
+
export const getTheme = () => {
|
|
235
|
+
return document.documentElement.getAttribute('data-theme') || 'light';
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Toggle between light and dark themes
|
|
240
|
+
*/
|
|
241
|
+
export const toggleTheme = () => {
|
|
242
|
+
const current = getTheme();
|
|
243
|
+
setTheme(current === 'dark' ? 'light' : 'dark');
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Available DaisyUI themes
|
|
248
|
+
*/
|
|
249
|
+
export const themes = [
|
|
250
|
+
'light', 'dark', 'cupcake', 'bumblebee', 'emerald', 'corporate',
|
|
251
|
+
'synthwave', 'retro', 'cyberpunk', 'valentine', 'halloween', 'garden',
|
|
252
|
+
'forest', 'aqua', 'lofi', 'pastel', 'fantasy', 'wireframe', 'black',
|
|
253
|
+
'luxury', 'dracula', 'cmyk', 'autumn', 'business', 'acid', 'lemonade',
|
|
254
|
+
'night', 'coffee', 'winter', 'dim', 'nord', 'sunset'
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Color variants available for components
|
|
259
|
+
*/
|
|
260
|
+
export const colors = ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'];
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Size variants available for components
|
|
264
|
+
*/
|
|
265
|
+
export const sizes = ['xs', 'sm', 'md', 'lg'];
|
|
266
|
+
|
|
267
|
+
// Note: Auto-initialization removed. Use one of:
|
|
268
|
+
// - LightviewX.initComponents() for shadow DOM components (recommended)
|
|
269
|
+
// - daisyui.init() for light DOM usage with global DaisyUI styles
|
|
270
|
+
|
|
271
|
+
export default {
|
|
272
|
+
init,
|
|
273
|
+
ensureDaisyUI,
|
|
274
|
+
ensureTailwind,
|
|
275
|
+
ensureThemes,
|
|
276
|
+
setTheme,
|
|
277
|
+
getTheme,
|
|
278
|
+
toggleTheme,
|
|
279
|
+
themes,
|
|
280
|
+
colors,
|
|
281
|
+
sizes,
|
|
282
|
+
// Shadow DOM / Adopted Stylesheets
|
|
283
|
+
getDaisyStyleSheet,
|
|
284
|
+
getComponentStyleSheet,
|
|
285
|
+
getDaisyStyleSheetSync,
|
|
286
|
+
getComponentStyleSheetSync,
|
|
287
|
+
preloadShadowStyles
|
|
288
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightview Accordion Component (DaisyUI)
|
|
3
|
+
* @see https://daisyui.com/components/accordion/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import '../daisyui.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Accordion - show/hide content, one item at a time
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {boolean} props.join - Use join style (connected items)
|
|
12
|
+
* @param {string} props.icon - 'arrow' | 'plus'
|
|
13
|
+
* @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
|
|
14
|
+
*/
|
|
15
|
+
const Accordion = (props = {}, ...children) => {
|
|
16
|
+
const { tags } = window.Lightview || {};
|
|
17
|
+
const LVX = window.LightviewX || {};
|
|
18
|
+
|
|
19
|
+
if (!tags) return null;
|
|
20
|
+
|
|
21
|
+
const { div, shadowDOM } = tags;
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
join = false,
|
|
25
|
+
useShadow,
|
|
26
|
+
class: className = '',
|
|
27
|
+
...rest
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
const classes = join ? ['join join-vertical w-full'] : ['space-y-2'];
|
|
31
|
+
if (className) classes.push(className);
|
|
32
|
+
|
|
33
|
+
const accordionEl = div({ class: classes.join(' '), ...rest }, ...children);
|
|
34
|
+
|
|
35
|
+
// Check if we should use shadow DOM
|
|
36
|
+
let usesShadow = false;
|
|
37
|
+
if (LVX.shouldUseShadow) {
|
|
38
|
+
usesShadow = LVX.shouldUseShadow(useShadow);
|
|
39
|
+
} else {
|
|
40
|
+
usesShadow = useShadow === true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (usesShadow) {
|
|
44
|
+
const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
|
|
45
|
+
|
|
46
|
+
const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
|
|
47
|
+
|
|
48
|
+
return div({ class: 'contents' },
|
|
49
|
+
shadowDOM({ mode: 'open', adoptedStyleSheets },
|
|
50
|
+
div({ 'data-theme': themeValue },
|
|
51
|
+
accordionEl
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return accordionEl;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Accordion Item
|
|
62
|
+
*/
|
|
63
|
+
Accordion.Item = (props = {}, ...children) => {
|
|
64
|
+
const { tags } = window.Lightview || {};
|
|
65
|
+
if (!tags) return null;
|
|
66
|
+
|
|
67
|
+
const { div, input } = tags;
|
|
68
|
+
|
|
69
|
+
const {
|
|
70
|
+
name = 'accordion',
|
|
71
|
+
icon = 'arrow',
|
|
72
|
+
join = false,
|
|
73
|
+
checked = false,
|
|
74
|
+
class: className = '',
|
|
75
|
+
...rest
|
|
76
|
+
} = props;
|
|
77
|
+
|
|
78
|
+
const classes = ['collapse'];
|
|
79
|
+
if (icon === 'arrow') classes.push('collapse-arrow');
|
|
80
|
+
else if (icon === 'plus') classes.push('collapse-plus');
|
|
81
|
+
classes.push('bg-base-100');
|
|
82
|
+
if (join) classes.push('join-item border-base-300 border');
|
|
83
|
+
else classes.push('border border-base-300 rounded-box');
|
|
84
|
+
if (className) classes.push(className);
|
|
85
|
+
|
|
86
|
+
return div({ class: classes.join(' '), ...rest },
|
|
87
|
+
input({ type: 'radio', name, checked }),
|
|
88
|
+
...children
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Accordion Title
|
|
94
|
+
*/
|
|
95
|
+
Accordion.Title = (props = {}, ...children) => {
|
|
96
|
+
const { tags } = window.Lightview || {};
|
|
97
|
+
if (!tags) return null;
|
|
98
|
+
|
|
99
|
+
const { class: className = '', ...rest } = props;
|
|
100
|
+
|
|
101
|
+
return tags.div({
|
|
102
|
+
class: `collapse-title font-semibold ${className}`.trim(),
|
|
103
|
+
...rest
|
|
104
|
+
}, ...children);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Accordion Content
|
|
109
|
+
*/
|
|
110
|
+
Accordion.Content = (props = {}, ...children) => {
|
|
111
|
+
const { tags } = window.Lightview || {};
|
|
112
|
+
if (!tags) return null;
|
|
113
|
+
|
|
114
|
+
const { class: className = '', ...rest } = props;
|
|
115
|
+
|
|
116
|
+
return tags.div({
|
|
117
|
+
class: `collapse-content text-sm ${className}`.trim(),
|
|
118
|
+
...rest
|
|
119
|
+
}, ...children);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const tags = window.Lightview.tags;
|
|
123
|
+
tags.Accordion = Accordion;
|
|
124
|
+
tags['Accordion.Item'] = Accordion.Item;
|
|
125
|
+
tags['Accordion.Title'] = Accordion.Title;
|
|
126
|
+
tags['Accordion.Content'] = Accordion.Content;
|
|
127
|
+
|
|
128
|
+
export default Accordion;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightview Alert Component (DaisyUI)
|
|
3
|
+
* @see https://daisyui.com/components/alert/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import '../daisyui.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Alert Component
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {string} props.color - 'info' | 'success' | 'warning' | 'error'
|
|
12
|
+
* @param {boolean} props.soft - Use soft/light variant
|
|
13
|
+
* @param {boolean} props.dash - Use dashed border
|
|
14
|
+
* @param {boolean} props.outline - Use outline style
|
|
15
|
+
* @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
|
|
16
|
+
*/
|
|
17
|
+
const Alert = (props = {}, ...children) => {
|
|
18
|
+
const { tags } = window.Lightview || {};
|
|
19
|
+
const LVX = window.LightviewX || {};
|
|
20
|
+
|
|
21
|
+
if (!tags) return null;
|
|
22
|
+
|
|
23
|
+
const { div, shadowDOM } = tags;
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
color,
|
|
27
|
+
soft = false,
|
|
28
|
+
dash = false,
|
|
29
|
+
outline = false,
|
|
30
|
+
icon,
|
|
31
|
+
useShadow,
|
|
32
|
+
class: className = '',
|
|
33
|
+
...rest
|
|
34
|
+
} = props;
|
|
35
|
+
|
|
36
|
+
const classes = ['alert'];
|
|
37
|
+
if (color) classes.push(`alert-${color}`);
|
|
38
|
+
if (soft) classes.push('alert-soft');
|
|
39
|
+
if (dash) classes.push('alert-dash');
|
|
40
|
+
if (outline) classes.push('alert-outline');
|
|
41
|
+
if (className) classes.push(className);
|
|
42
|
+
|
|
43
|
+
const iconSvg = icon ? getAlertIcon(icon || color) : null;
|
|
44
|
+
|
|
45
|
+
let alertEl;
|
|
46
|
+
if (iconSvg) {
|
|
47
|
+
alertEl = div({ role: 'alert', class: classes.join(' '), ...rest },
|
|
48
|
+
iconSvg,
|
|
49
|
+
...children
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
alertEl = div({ role: 'alert', class: classes.join(' '), ...rest }, ...children);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if we should use shadow DOM
|
|
56
|
+
let usesShadow = false;
|
|
57
|
+
if (LVX.shouldUseShadow) {
|
|
58
|
+
usesShadow = LVX.shouldUseShadow(useShadow);
|
|
59
|
+
} else {
|
|
60
|
+
usesShadow = useShadow === true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (usesShadow) {
|
|
64
|
+
const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
|
|
65
|
+
|
|
66
|
+
const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
|
|
67
|
+
|
|
68
|
+
return div({ class: 'contents' },
|
|
69
|
+
shadowDOM({ mode: 'open', adoptedStyleSheets },
|
|
70
|
+
div({ 'data-theme': themeValue },
|
|
71
|
+
alertEl
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return alertEl;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
function getAlertIcon(type) {
|
|
81
|
+
const { tags } = window.Lightview || {};
|
|
82
|
+
if (!tags) return null;
|
|
83
|
+
|
|
84
|
+
const icons = {
|
|
85
|
+
info: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>',
|
|
86
|
+
success: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>',
|
|
87
|
+
warning: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>',
|
|
88
|
+
error: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (!icons[type]) return null;
|
|
92
|
+
|
|
93
|
+
return tags.svg({
|
|
94
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
95
|
+
class: 'h-6 w-6 shrink-0 stroke-current',
|
|
96
|
+
fill: 'none',
|
|
97
|
+
viewBox: '0 0 24 24',
|
|
98
|
+
innerHTML: icons[type]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
window.Lightview.tags.Alert = Alert;
|
|
103
|
+
|
|
104
|
+
// Register as Custom Element
|
|
105
|
+
if (window.LightviewX?.createCustomElement) {
|
|
106
|
+
const AlertElement = window.LightviewX.createCustomElement(Alert);
|
|
107
|
+
if (!customElements.get('lv-alert')) {
|
|
108
|
+
customElements.define('lv-alert', AlertElement);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default Alert;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightview Avatar Component (DaisyUI)
|
|
3
|
+
* @see https://daisyui.com/components/avatar/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import '../daisyui.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Avatar Component
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {string} props.src - Image source URL
|
|
12
|
+
* @param {string} props.alt - Alt text for image
|
|
13
|
+
* @param {string} props.placeholder - Text to show when no image
|
|
14
|
+
* @param {string} props.size - Size in pixels or class
|
|
15
|
+
* @param {string} props.shape - 'circle' | 'rounded' | 'squircle' | 'hexagon' | 'triangle'
|
|
16
|
+
* @param {boolean} props.ring - Add ring border
|
|
17
|
+
* @param {string} props.ringColor - Ring color class
|
|
18
|
+
* @param {boolean} props.online - Show online indicator
|
|
19
|
+
* @param {boolean} props.offline - Show offline indicator
|
|
20
|
+
* @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
|
|
21
|
+
*/
|
|
22
|
+
const Avatar = (props = {}, ...children) => {
|
|
23
|
+
const { tags } = window.Lightview || {};
|
|
24
|
+
const LVX = window.LightviewX || {};
|
|
25
|
+
|
|
26
|
+
if (!tags) return null;
|
|
27
|
+
|
|
28
|
+
const { div, img, shadowDOM } = tags;
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
src,
|
|
32
|
+
alt = 'Avatar',
|
|
33
|
+
placeholder,
|
|
34
|
+
size = 'w-12',
|
|
35
|
+
shape = 'circle',
|
|
36
|
+
ring = false,
|
|
37
|
+
ringColor = 'ring-primary',
|
|
38
|
+
online = false,
|
|
39
|
+
offline = false,
|
|
40
|
+
useShadow,
|
|
41
|
+
class: className = '',
|
|
42
|
+
...rest
|
|
43
|
+
} = props;
|
|
44
|
+
|
|
45
|
+
const wrapperClasses = ['avatar'];
|
|
46
|
+
if (online) wrapperClasses.push('online');
|
|
47
|
+
if (offline) wrapperClasses.push('offline');
|
|
48
|
+
if (placeholder && !src) wrapperClasses.push('placeholder');
|
|
49
|
+
if (className) wrapperClasses.push(className);
|
|
50
|
+
|
|
51
|
+
const innerClasses = [];
|
|
52
|
+
const innerStyle = {};
|
|
53
|
+
|
|
54
|
+
// Check if size is a Tailwind class (w-*) or a direct measurement
|
|
55
|
+
if (size) {
|
|
56
|
+
if (/^w-(\d+)$/.test(size)) {
|
|
57
|
+
// Workaround: Map w- classes to explicit styles as Tailwind classes might be missing
|
|
58
|
+
const num = parseInt(size.split('-')[1]);
|
|
59
|
+
const remValue = num * 0.25;
|
|
60
|
+
innerStyle.width = `${remValue}rem`;
|
|
61
|
+
innerStyle.height = `${remValue}rem`;
|
|
62
|
+
innerClasses.push(size);
|
|
63
|
+
} else if (/^w-/.test(size)) {
|
|
64
|
+
innerClasses.push(size);
|
|
65
|
+
} else {
|
|
66
|
+
// Treat as direct measurement (e.g., 64px, 4rem)
|
|
67
|
+
innerStyle.width = size;
|
|
68
|
+
innerStyle.height = size;
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
innerClasses.push('w-12');
|
|
72
|
+
innerStyle.width = '3rem';
|
|
73
|
+
innerStyle.height = '3rem';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (ring) {
|
|
77
|
+
innerClasses.push('ring', ringColor, 'ring-offset-base-100', 'ring-offset-2');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Shape classes
|
|
81
|
+
if (shape === 'circle') innerClasses.push('rounded-full');
|
|
82
|
+
else if (shape === 'rounded') innerClasses.push('rounded');
|
|
83
|
+
else if (shape === 'squircle') innerClasses.push('mask mask-squircle');
|
|
84
|
+
else if (shape === 'hexagon') innerClasses.push('mask mask-hexagon');
|
|
85
|
+
else if (shape === 'triangle') innerClasses.push('mask mask-triangle');
|
|
86
|
+
|
|
87
|
+
let avatarEl;
|
|
88
|
+
if (src) {
|
|
89
|
+
avatarEl = div({ class: wrapperClasses.join(' '), ...rest },
|
|
90
|
+
div({ class: innerClasses.join(' '), style: innerStyle },
|
|
91
|
+
img({ src, alt, style: 'width: 100%; height: 100%; object-fit: cover;' })
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
} else {
|
|
95
|
+
// Placeholder avatar
|
|
96
|
+
// Use flexbox to center the placeholder text
|
|
97
|
+
innerClasses.push('bg-neutral text-neutral-content flex items-center justify-center');
|
|
98
|
+
|
|
99
|
+
// Support custom background/text colors via CSS variables if provided in style
|
|
100
|
+
innerStyle.backgroundColor = 'var(--lv-avatar-bg)';
|
|
101
|
+
innerStyle.color = 'var(--lv-avatar-text)';
|
|
102
|
+
innerStyle.display = 'flex';
|
|
103
|
+
innerStyle.alignItems = 'center';
|
|
104
|
+
innerStyle.justifyContent = 'center';
|
|
105
|
+
|
|
106
|
+
avatarEl = div({ class: wrapperClasses.join(' '), ...rest },
|
|
107
|
+
div({
|
|
108
|
+
class: innerClasses.join(' '),
|
|
109
|
+
style: innerStyle
|
|
110
|
+
},
|
|
111
|
+
tags.span({}, placeholder || children[0] || '?')
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check if we should use shadow DOM
|
|
117
|
+
let usesShadow = false;
|
|
118
|
+
if (LVX.shouldUseShadow) {
|
|
119
|
+
usesShadow = LVX.shouldUseShadow(useShadow);
|
|
120
|
+
} else {
|
|
121
|
+
usesShadow = useShadow === true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (usesShadow) {
|
|
125
|
+
const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
|
|
126
|
+
|
|
127
|
+
const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
|
|
128
|
+
|
|
129
|
+
return div({ class: 'contents' },
|
|
130
|
+
shadowDOM({ mode: 'open', adoptedStyleSheets },
|
|
131
|
+
div({ 'data-theme': themeValue },
|
|
132
|
+
avatarEl
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return avatarEl;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Avatar Group
|
|
143
|
+
*/
|
|
144
|
+
Avatar.Group = (props = {}, ...children) => {
|
|
145
|
+
const { tags } = window.Lightview || {};
|
|
146
|
+
if (!tags) return null;
|
|
147
|
+
|
|
148
|
+
const { class: className = '', ...rest } = props;
|
|
149
|
+
|
|
150
|
+
return tags.div({
|
|
151
|
+
class: `avatar-group -space-x-6 rtl:space-x-reverse ${className}`.trim(),
|
|
152
|
+
...rest
|
|
153
|
+
}, ...children);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const tags = window.Lightview.tags;
|
|
157
|
+
tags.Avatar = Avatar;
|
|
158
|
+
tags['Avatar.Group'] = Avatar.Group;
|
|
159
|
+
|
|
160
|
+
// Register as Custom Elements
|
|
161
|
+
if (window.LightviewX?.createCustomElement) {
|
|
162
|
+
if (!customElements.get('lv-avatar')) {
|
|
163
|
+
customElements.define('lv-avatar', window.LightviewX.createCustomElement(Avatar));
|
|
164
|
+
}
|
|
165
|
+
if (!customElements.get('lv-avatar-group')) {
|
|
166
|
+
customElements.define('lv-avatar-group', window.LightviewX.createCustomElement(Avatar.Group));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default Avatar;
|