@k3000/ce 0.1.0 → 0.2.1
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 +12 -6
- package/app/comp/comp-test.mjs +1 -1
- package/app/comp/nav-bar.mjs +1 -1
- package/app/comp/page-container.mjs +1 -1
- package/app/comp/tab-bar.mjs +1 -1
- package/app/index.html +1 -1
- package/bin.mjs +48 -0
- package/ce.mjs +1 -0
- package/comm/router-view.mjs +1 -1
- package/console/components/common-action-button.mjs +1 -1
- package/console/components/common-header.mjs +1 -1
- package/console/components/common-input.mjs +1 -1
- package/console/components/common-modal.mjs +1 -1
- package/console/components/common-pagination.mjs +1 -1
- package/console/components/common-toolbar.mjs +1 -1
- package/console/components/div-test.mjs +11 -0
- package/console/components/g-button.mjs +79 -0
- package/console/components/g-cell.mjs +129 -0
- package/console/components/g-col.mjs +38 -0
- package/console/components/g-field.mjs +162 -0
- package/console/components/g-form.mjs +11 -0
- package/console/components/g-icon.mjs +82 -0
- package/console/components/g-image.mjs +127 -0
- package/console/components/g-popup.mjs +130 -0
- package/console/components/g-row.mjs +120 -0
- package/console/components/g-space.mjs +21 -0
- package/console/components/g-toast.mjs +116 -0
- package/console/components/layout-header.mjs +0 -179
- package/console/components/layout-menu.mjs +99 -2
- package/console/index.html +2 -1
- package/console/index2.html +46 -0
- package/console/pages/demo-button.mjs +116 -0
- package/console/pages/demo-cell.mjs +59 -0
- package/console/pages/demo-field.mjs +67 -0
- package/console/pages/demo-icon.mjs +200 -0
- package/console/pages/demo-image.mjs +113 -0
- package/console/pages/demo-layout.mjs +141 -0
- package/console/pages/demo-popup.mjs +158 -0
- package/console/pages/demo-space.mjs +51 -0
- package/console/pages/demo-toast.mjs +66 -0
- package/index.mjs +8 -1
- package/package.json +8 -2
- package/test.html +15 -0
- package/console/components/console-header.mjs +0 -26
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useAttr, useRef } from "../../ce.mjs";
|
|
2
|
+
|
|
3
|
+
export const innerHTML = `
|
|
4
|
+
<svg ref="svg" class="{{classStr}}" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
5
|
+
<path ref="path" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
|
|
6
|
+
</svg>
|
|
7
|
+
<div class="{{hasDot ? '' : 'hidden'}} absolute top-0 right-0 transform translate-x-[20%] -translate-y-[20%] flex h-2.5 w-2.5">
|
|
8
|
+
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
|
|
9
|
+
<span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-red-500 border-[1.5px] border-white dark:border-gray-800"></span>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="{{badge ? '' : 'hidden'}} absolute top-0 right-0 transform translate-x-[30%] -translate-y-[30%] flex items-center justify-center z-10">
|
|
12
|
+
<span class="relative inline-flex rounded-full bg-red-500 text-white text-[10px] items-center justify-center px-1 min-w-[16px] h-[16px] border-[1.5px] border-white dark:border-gray-800 font-medium whitespace-nowrap leading-none shadow-sm">{{badge}}</span>
|
|
13
|
+
</div>
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
// Icon paths mapped by name
|
|
17
|
+
const icons = {
|
|
18
|
+
system: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',
|
|
19
|
+
system_inner: 'M15 12a3 3 0 11-6 0 3 3 0 016 0z', // Need to add multiple paths if needed, here we simplify or append
|
|
20
|
+
user: 'M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z',
|
|
21
|
+
role: 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.956 11.956 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z',
|
|
22
|
+
menu: 'M4 6h16M4 12h16M4 18h16',
|
|
23
|
+
tools: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z', // Combining paths for simplicity for now
|
|
24
|
+
search: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z',
|
|
25
|
+
button: 'M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z',
|
|
26
|
+
home: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6',
|
|
27
|
+
edit: 'M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z',
|
|
28
|
+
delete: 'M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16',
|
|
29
|
+
add: 'M12 4v16m8-8H4',
|
|
30
|
+
check: 'M5 13l4 4L19 7',
|
|
31
|
+
close: 'M6 18L18 6M6 6l12 12',
|
|
32
|
+
info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
33
|
+
warning: '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',
|
|
34
|
+
view: 'M15 12a3 3 0 11-6 0 3 3 0 016 0zM2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z',
|
|
35
|
+
logout: 'M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1',
|
|
36
|
+
upload: 'M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12',
|
|
37
|
+
download: 'M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4',
|
|
38
|
+
refresh: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default class extends HTMLElement {
|
|
42
|
+
// name = ''
|
|
43
|
+
classStr = ''
|
|
44
|
+
size = ''
|
|
45
|
+
color = ''
|
|
46
|
+
hasDot = false
|
|
47
|
+
badge = ''
|
|
48
|
+
|
|
49
|
+
constructor() {
|
|
50
|
+
super()
|
|
51
|
+
this.style.display = 'inline-flex'
|
|
52
|
+
this.style.alignItems = 'center'
|
|
53
|
+
this.style.justifyContent = 'center'
|
|
54
|
+
this.style.position = 'relative'
|
|
55
|
+
|
|
56
|
+
const attr = useAttr(this)
|
|
57
|
+
this.name = this.name || attr.name || ''
|
|
58
|
+
this.size = attr.size || ''
|
|
59
|
+
this.color = attr.color || ''
|
|
60
|
+
this.badge = attr.badge || ''
|
|
61
|
+
this.hasDot = !this.badge && attr.dot !== undefined && attr.dot !== 'false'
|
|
62
|
+
this.classStr = attr.class || (this.size ? '' : 'w-5 h-5')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ready() {
|
|
66
|
+
const { path, svg } = useRef(this)
|
|
67
|
+
|
|
68
|
+
if (this.size) {
|
|
69
|
+
svg.style.width = this.size
|
|
70
|
+
svg.style.height = this.size
|
|
71
|
+
}
|
|
72
|
+
if (this.color) {
|
|
73
|
+
svg.style.color = this.color
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (this.name && icons[this.name]) {
|
|
77
|
+
path.setAttribute('d', icons[this.name])
|
|
78
|
+
} else {
|
|
79
|
+
path.setAttribute('d', 'M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z') // default info icon
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useAttr, bind, useRef } from "../../ce.mjs";
|
|
2
|
+
|
|
3
|
+
const htmlStr = `
|
|
4
|
+
<div class="relative overflow-hidden inline-flex items-center justify-center {{wrapClass}} {{isLoading ? 'bg-white/20 dark:bg-gray-800/20 animate-pulse' : 'bg-white/10 dark:bg-gray-800/10'}} transition-colors" style="{{wrapStyle}}">
|
|
5
|
+
<!-- Image -->
|
|
6
|
+
<img ref="img" src="{{src}}" alt="{{alt}}" class="w-full h-full object-{{fit}} {{isLoaded ? 'opacity-100' : 'opacity-0'}} transition-opacity duration-300 {{imgClass}}" style="{{imgStyle}}" {{lazy ? 'loading="lazy"' : ''}} />
|
|
7
|
+
|
|
8
|
+
<!-- Loading State -->
|
|
9
|
+
<div class="absolute inset-0 flex flex-col items-center justify-center text-gray-400 dark:text-gray-500 {{isLoading ? '' : 'hidden'}} bg-white/40 dark:bg-gray-800/40 backdrop-blur-sm">
|
|
10
|
+
<slot name="loading">
|
|
11
|
+
<svg class="w-6 h-6 animate-spin text-blue-500/70" fill="none" viewBox="0 0 24 24">
|
|
12
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
13
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
14
|
+
</svg>
|
|
15
|
+
</slot>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Error State -->
|
|
19
|
+
<div class="absolute inset-0 flex flex-col items-center justify-center text-gray-400 dark:text-gray-500 {{isError ? '' : 'hidden'}} bg-white/40 dark:bg-gray-800/40 backdrop-blur-sm">
|
|
20
|
+
<slot name="error">
|
|
21
|
+
<g-icon name="view" class="w-6 h-6 mb-1 opacity-50"></g-icon>
|
|
22
|
+
<span class="text-[10px] sm:text-xs">加载失败</span>
|
|
23
|
+
</slot>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
export default class extends HTMLElement {
|
|
29
|
+
src = ''
|
|
30
|
+
alt = ''
|
|
31
|
+
fit = 'cover' // 'fill', 'contain', 'cover', 'none', 'scale-down'
|
|
32
|
+
position = '' // 'top', 'bottom', 'center', 'left', 'right', or string coords
|
|
33
|
+
lazy = false
|
|
34
|
+
wrapClass = ''
|
|
35
|
+
wrapStyle = ''
|
|
36
|
+
imgClass = ''
|
|
37
|
+
imgStyle = ''
|
|
38
|
+
width = ''
|
|
39
|
+
height = ''
|
|
40
|
+
|
|
41
|
+
isLoaded = false
|
|
42
|
+
isError = false
|
|
43
|
+
isLoading = true
|
|
44
|
+
|
|
45
|
+
constructor() {
|
|
46
|
+
super()
|
|
47
|
+
this.style.display = 'inline-block'
|
|
48
|
+
const attr = useAttr(this)
|
|
49
|
+
|
|
50
|
+
this.src = attr.src || ''
|
|
51
|
+
this.alt = attr.alt || ''
|
|
52
|
+
this.fit = attr.fit || 'cover'
|
|
53
|
+
this.position = attr.position || ''
|
|
54
|
+
this.lazy = attr.lazy !== undefined && attr.lazy !== 'false'
|
|
55
|
+
this.width = attr.width || ''
|
|
56
|
+
this.height = attr.height || ''
|
|
57
|
+
|
|
58
|
+
let baseClass = attr.class || ''
|
|
59
|
+
|
|
60
|
+
// If neither width/height nor explicit class dimensions are provided, default to a sensible size
|
|
61
|
+
if (!this.width && !this.height && !baseClass.includes('w-') && !baseClass.includes('h-')) {
|
|
62
|
+
baseClass += ' w-24 h-24 sm:w-32 sm:h-32'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.width) this.wrapStyle += `width: ${this.width}; `
|
|
66
|
+
if (this.height) this.wrapStyle += `height: ${this.height}; `
|
|
67
|
+
|
|
68
|
+
if (this.position) this.imgStyle += `object-position: ${this.position}; `
|
|
69
|
+
|
|
70
|
+
// Handling shapes
|
|
71
|
+
if (attr.round === 'true') {
|
|
72
|
+
baseClass += ' rounded-full'
|
|
73
|
+
} else if (attr.radius) {
|
|
74
|
+
baseClass += ` rounded-[${attr.radius}]`
|
|
75
|
+
} else if (!baseClass.includes('rounded')) {
|
|
76
|
+
baseClass += ' rounded-2xl' // Default glass rounded corner
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Add glass border
|
|
80
|
+
this.wrapClass = baseClass + ' backdrop-blur-md border border-white/60 dark:border-white/10 shadow-[inner_0_1px_1px_rgba(255,255,255,0.8),0_4px_12px_rgba(0,0,0,0.05)] dark:shadow-[inner_0_1px_1px_rgba(255,255,255,0.1),0_4px_12px_rgba(0,0,0,0.2)]'
|
|
81
|
+
|
|
82
|
+
// Pass some specific classes directly to img if needed based on shape constraints
|
|
83
|
+
if (baseClass.includes('rounded-full')) {
|
|
84
|
+
this.imgClass = 'rounded-full'
|
|
85
|
+
} else if (baseClass.includes('rounded-2xl')) {
|
|
86
|
+
this.imgClass = 'rounded-2xl'
|
|
87
|
+
} else if (attr.radius) {
|
|
88
|
+
this.imgClass = `rounded-[${attr.radius}]`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!this.src) {
|
|
92
|
+
this.isLoading = false
|
|
93
|
+
this.isError = true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ready() {
|
|
98
|
+
const nodes = bind(htmlStr).create(this)
|
|
99
|
+
this.appendChild(nodes)
|
|
100
|
+
|
|
101
|
+
const { img } = useRef(this)
|
|
102
|
+
|
|
103
|
+
if (this.src) {
|
|
104
|
+
// Check if image is already cached/loaded completely
|
|
105
|
+
if (img.complete && img.naturalHeight !== 0) {
|
|
106
|
+
this.setLoaded()
|
|
107
|
+
} else {
|
|
108
|
+
img.onload = () => this.setLoaded()
|
|
109
|
+
img.onerror = () => this.setError()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setLoaded() {
|
|
115
|
+
this.isLoading = false
|
|
116
|
+
this.isLoaded = true
|
|
117
|
+
this.isError = false
|
|
118
|
+
if (this.update) this.update()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setError() {
|
|
122
|
+
this.isLoading = false
|
|
123
|
+
this.isLoaded = false
|
|
124
|
+
this.isError = true
|
|
125
|
+
if (this.update) this.update()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useAttr, bind } from "../../ce.mjs";
|
|
2
|
+
import {useWatch} from "../../index.mjs";
|
|
3
|
+
|
|
4
|
+
let zIndex = 2000
|
|
5
|
+
|
|
6
|
+
export const innerHTML = `
|
|
7
|
+
<div class="fixed inset-0 flex {{wrapClass}} transition-all duration-300 {{visible ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}}" style="z-index: {{zIndex}};">
|
|
8
|
+
<!-- Overlay -->
|
|
9
|
+
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm {{overlay !== 'false' ? '' : 'hidden'}}" onclick="{{onOverlayClick}}"></div>
|
|
10
|
+
|
|
11
|
+
<!-- Content pane -->
|
|
12
|
+
<div class="relative bg-white/80 dark:bg-gray-800/80 backdrop-blur-2xl shadow-2xl transition-all duration-300 {{contentClass}} {{visible ? visibleClass : hiddenClass}}" onclick="event.stopPropagation()">
|
|
13
|
+
<slot></slot>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
`
|
|
17
|
+
|
|
18
|
+
export default class extends HTMLElement {
|
|
19
|
+
visible = false
|
|
20
|
+
position = 'center'
|
|
21
|
+
overlay = 'true'
|
|
22
|
+
closeOnClickOverlay = 'true'
|
|
23
|
+
round = 'false'
|
|
24
|
+
zIndex = 2000
|
|
25
|
+
|
|
26
|
+
wrapClass = 'items-center justify-center p-4'
|
|
27
|
+
contentClass = 'rounded-2xl max-w-full max-h-full overflow-y-auto'
|
|
28
|
+
visibleClass = 'opacity-100 scale-100 translate-y-0 translate-x-0'
|
|
29
|
+
hiddenClass = 'opacity-0 scale-95'
|
|
30
|
+
|
|
31
|
+
constructor() {
|
|
32
|
+
super()
|
|
33
|
+
this.style.display = 'contents'
|
|
34
|
+
const attr = useAttr(this)
|
|
35
|
+
|
|
36
|
+
this.position = attr.position || 'center'
|
|
37
|
+
this.overlay = attr.overlay || 'true'
|
|
38
|
+
this.closeOnClickOverlay = attr['close-on-click-overlay'] || 'true'
|
|
39
|
+
this.round = attr.round || 'false'
|
|
40
|
+
|
|
41
|
+
this._initClasses()
|
|
42
|
+
|
|
43
|
+
const watch = useWatch(this)
|
|
44
|
+
|
|
45
|
+
watch({
|
|
46
|
+
status: (status, value) => {
|
|
47
|
+
|
|
48
|
+
if (status === value) return
|
|
49
|
+
|
|
50
|
+
if (status) {
|
|
51
|
+
|
|
52
|
+
this.open()
|
|
53
|
+
|
|
54
|
+
} else {
|
|
55
|
+
|
|
56
|
+
this.close()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ready() {
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_initClasses() {
|
|
66
|
+
const isRound = this.round === 'true'
|
|
67
|
+
switch (this.position) {
|
|
68
|
+
case 'bottom':
|
|
69
|
+
this.wrapClass = 'items-end justify-center'
|
|
70
|
+
this.contentClass = `w-full max-h-[90vh] overflow-y-auto ${isRound ? 'rounded-t-3xl' : ''}`
|
|
71
|
+
this.visibleClass = 'translate-y-0 opacity-100'
|
|
72
|
+
this.hiddenClass = 'translate-y-full opacity-0'
|
|
73
|
+
break
|
|
74
|
+
case 'top':
|
|
75
|
+
this.wrapClass = 'items-start justify-center'
|
|
76
|
+
this.contentClass = `w-full max-h-[90vh] overflow-y-auto ${isRound ? 'rounded-b-3xl' : ''}`
|
|
77
|
+
this.visibleClass = 'translate-y-0 opacity-100'
|
|
78
|
+
this.hiddenClass = '-translate-y-full opacity-0'
|
|
79
|
+
break
|
|
80
|
+
case 'left':
|
|
81
|
+
this.wrapClass = 'items-center justify-start'
|
|
82
|
+
this.contentClass = `h-full max-w-[90vw] overflow-y-auto ${isRound ? 'rounded-r-3xl' : ''}`
|
|
83
|
+
this.visibleClass = 'translate-x-0 opacity-100'
|
|
84
|
+
this.hiddenClass = '-translate-x-full opacity-0'
|
|
85
|
+
break
|
|
86
|
+
case 'right':
|
|
87
|
+
this.wrapClass = 'items-center justify-end'
|
|
88
|
+
this.contentClass = `h-full max-w-[90vw] overflow-y-auto ${isRound ? 'rounded-l-3xl' : ''}`
|
|
89
|
+
this.visibleClass = 'translate-x-0 opacity-100'
|
|
90
|
+
this.hiddenClass = 'translate-x-full opacity-0'
|
|
91
|
+
break
|
|
92
|
+
case 'center':
|
|
93
|
+
default:
|
|
94
|
+
this.wrapClass = 'items-center justify-center p-4' // safe area
|
|
95
|
+
this.contentClass = `max-w-[90vw] max-h-[90vh] overflow-y-auto ${isRound ? 'rounded-3xl' : ''}`
|
|
96
|
+
this.visibleClass = 'opacity-100 scale-100'
|
|
97
|
+
this.hiddenClass = 'opacity-0 scale-95'
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
open() {
|
|
103
|
+
this.zIndex = zIndex++
|
|
104
|
+
this.visible = true
|
|
105
|
+
if (this.update) this.update()
|
|
106
|
+
if (this.onopen) this.onopen()
|
|
107
|
+
|
|
108
|
+
// Prevent body scroll
|
|
109
|
+
document.body.style.overflow = 'hidden'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
close() {
|
|
113
|
+
this.visible = false
|
|
114
|
+
if (this.update) this.update()
|
|
115
|
+
if (this.onclose) this.onclose()
|
|
116
|
+
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
const hasOpenPopups = Array.from(document.querySelectorAll('g-popup')).some(p => p.visible)
|
|
119
|
+
if (!hasOpenPopups) {
|
|
120
|
+
document.body.style.overflow = ''
|
|
121
|
+
}
|
|
122
|
+
}, 300)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
onOverlayClick() {
|
|
126
|
+
if (this.closeOnClickOverlay === 'true') {
|
|
127
|
+
this.close()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useAttr } from "../../ce.mjs";
|
|
2
|
+
|
|
3
|
+
export default class extends HTMLElement {
|
|
4
|
+
constructor() {
|
|
5
|
+
super()
|
|
6
|
+
this.style.display = 'flex'
|
|
7
|
+
this.style.flexWrap = 'wrap'
|
|
8
|
+
this.style.width = '100%'
|
|
9
|
+
this.style.boxSizing = 'border-box'
|
|
10
|
+
|
|
11
|
+
const attr = useAttr(this)
|
|
12
|
+
|
|
13
|
+
const justify = attr.justify || 'start'
|
|
14
|
+
const align = attr.align || 'top'
|
|
15
|
+
const gutter = attr.gutter || '0'
|
|
16
|
+
const classStr = attr.class || ''
|
|
17
|
+
|
|
18
|
+
// Map justify to flexbox
|
|
19
|
+
const justifyMap = {
|
|
20
|
+
'start': 'flex-start',
|
|
21
|
+
'end': 'flex-end',
|
|
22
|
+
'center': 'center',
|
|
23
|
+
'space-around': 'space-around',
|
|
24
|
+
'space-between': 'space-between'
|
|
25
|
+
}
|
|
26
|
+
this.style.justifyContent = justifyMap[justify] || 'flex-start'
|
|
27
|
+
|
|
28
|
+
// Map align to flexbox
|
|
29
|
+
const alignMap = {
|
|
30
|
+
'top': 'flex-start',
|
|
31
|
+
'middle': 'center',
|
|
32
|
+
'bottom': 'flex-end'
|
|
33
|
+
}
|
|
34
|
+
this.style.alignItems = alignMap[align] || 'flex-start'
|
|
35
|
+
|
|
36
|
+
// Handle gutter logic via context
|
|
37
|
+
// Gutter can be "20" or "[20, 30]"
|
|
38
|
+
let gutterX = 0
|
|
39
|
+
let gutterY = 0
|
|
40
|
+
|
|
41
|
+
if (gutter) {
|
|
42
|
+
if (gutter.startsWith('[')) {
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(gutter)
|
|
45
|
+
if (Array.isArray(parsed) && parsed.length >= 2) {
|
|
46
|
+
gutterX = Number(parsed[0])
|
|
47
|
+
gutterY = Number(parsed[1])
|
|
48
|
+
}
|
|
49
|
+
} catch (e) { }
|
|
50
|
+
} else {
|
|
51
|
+
gutterX = Number(gutter)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (gutterX || gutterY) {
|
|
56
|
+
const mx = gutterX ? `-${gutterX / 2}px` : '0'
|
|
57
|
+
const my = gutterY ? `-${gutterY / 2}px` : '0'
|
|
58
|
+
|
|
59
|
+
// Apply negative margins to row host
|
|
60
|
+
this.style.marginLeft = mx
|
|
61
|
+
this.style.marginRight = mx
|
|
62
|
+
this.style.marginTop = my
|
|
63
|
+
this.style.marginBottom = my
|
|
64
|
+
|
|
65
|
+
// Create a mutation observer to pass gutter context to children <g-col> dynamically or initially
|
|
66
|
+
this.xPadding = gutterX ? `${gutterX / 2}px` : '0'
|
|
67
|
+
this.yPadding = gutterY ? `${gutterY / 2}px` : '0'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (classStr) {
|
|
71
|
+
this.className = this.className ? this.className + ' ' + classStr : classStr
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Listen to children additions since standard Web Components don't easily do React-style context down effortlessly unless via MutationObserver or passing via DOM
|
|
75
|
+
this._setupObserver()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_setupObserver() {
|
|
79
|
+
this._observer = new MutationObserver((mutations) => {
|
|
80
|
+
if (!this.xPadding && !this.yPadding) return;
|
|
81
|
+
|
|
82
|
+
mutations.forEach((mutation) => {
|
|
83
|
+
if (mutation.type === 'childList') {
|
|
84
|
+
mutation.addedNodes.forEach(node => {
|
|
85
|
+
this._applyGutterToChild(node)
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
_applyGutterToChild(node) {
|
|
93
|
+
if (node.tagName === 'G-COL') {
|
|
94
|
+
if (this.xPadding) {
|
|
95
|
+
node.style.paddingLeft = this.xPadding
|
|
96
|
+
node.style.paddingRight = this.xPadding
|
|
97
|
+
}
|
|
98
|
+
if (this.yPadding) {
|
|
99
|
+
node.style.paddingTop = this.yPadding
|
|
100
|
+
node.style.paddingBottom = this.yPadding
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
connectedCallback() {
|
|
106
|
+
if (this._observer) {
|
|
107
|
+
this._observer.observe(this, { childList: true, subtree: false })
|
|
108
|
+
}
|
|
109
|
+
// Initially apply to existing children
|
|
110
|
+
if (this.xPadding || this.yPadding) {
|
|
111
|
+
Array.from(this.children).forEach(child => this._applyGutterToChild(child))
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
disconnectedCallback() {
|
|
116
|
+
if (this._observer) {
|
|
117
|
+
this._observer.disconnect()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {useAttr} from "../../index.mjs";
|
|
2
|
+
|
|
3
|
+
export const shadowRoot = `
|
|
4
|
+
<style>
|
|
5
|
+
div {
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: {{gap || '8px'}};
|
|
8
|
+
flex-direction: {{direction}}
|
|
9
|
+
}
|
|
10
|
+
</style>
|
|
11
|
+
<div>
|
|
12
|
+
<slot></slot>
|
|
13
|
+
</div>
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
export default class extends HTMLElement {
|
|
17
|
+
constructor() {
|
|
18
|
+
super()
|
|
19
|
+
Object.assign(this, useAttr(this))
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useAttr } from "../../ce.mjs";
|
|
2
|
+
|
|
3
|
+
let zIndex = 5000
|
|
4
|
+
|
|
5
|
+
export const innerHTML = `
|
|
6
|
+
<div class="toast-container fixed top-[45%] left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center gap-3 pointer-events-none" style="z-index: {{zIndex}};">
|
|
7
|
+
<slot></slot>
|
|
8
|
+
</div>
|
|
9
|
+
`
|
|
10
|
+
|
|
11
|
+
export default class extends HTMLElement {
|
|
12
|
+
constructor() {
|
|
13
|
+
super()
|
|
14
|
+
// this.style.display = 'contents'
|
|
15
|
+
this.zIndex = zIndex;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ready() {
|
|
19
|
+
// Expose to window globally
|
|
20
|
+
window.$toast = {
|
|
21
|
+
show: (msg, type = 'text', duration = 2000) => this.show(msg, type, duration),
|
|
22
|
+
success: (msg, duration = 2000) => this.show(msg, 'success', duration),
|
|
23
|
+
error: (msg, duration = 2000) => this.show(msg, 'error', duration),
|
|
24
|
+
warning: (msg, duration = 2000) => this.show(msg, 'warning', duration),
|
|
25
|
+
loading: (msg, duration = 0) => this.show(msg, 'loading', duration),
|
|
26
|
+
hide: () => this.hideAll()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
show(msg, type = 'text', duration = 2000) {
|
|
31
|
+
let container = this.querySelector('.toast-container') || this.firstElementChild;
|
|
32
|
+
if (!container) return;
|
|
33
|
+
|
|
34
|
+
const item = document.createElement('div');
|
|
35
|
+
|
|
36
|
+
let iconHtml = '';
|
|
37
|
+
if (type === 'success') {
|
|
38
|
+
iconHtml = '<svg class="w-5 h-5 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>';
|
|
39
|
+
} else if (type === 'error') {
|
|
40
|
+
iconHtml = '<svg class="w-5 h-5 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>';
|
|
41
|
+
} else if (type === 'warning') {
|
|
42
|
+
iconHtml = '<svg class="w-5 h-5 text-yellow-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><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" /></svg>';
|
|
43
|
+
} else if (type === 'loading') {
|
|
44
|
+
iconHtml = `<svg class="w-5 h-5 text-blue-400 animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
item.className = 'bg-gray-800/90 dark:bg-gray-200/90 text-white dark:text-gray-900 px-5 py-3 rounded-xl shadow-2xl backdrop-blur-md flex items-center gap-3 pointer-events-auto transition-all duration-300 transform scale-95 opacity-0 max-w-[80vw] !m-0 !mt-0 !mb-0 overflow-hidden';
|
|
48
|
+
|
|
49
|
+
// Starts with 0 height so the container shifts smoothly
|
|
50
|
+
item.style.maxHeight = '0px';
|
|
51
|
+
item.style.paddingTop = '0px';
|
|
52
|
+
item.style.paddingBottom = '0px';
|
|
53
|
+
item.style.opacity = '0';
|
|
54
|
+
|
|
55
|
+
item.innerHTML = `
|
|
56
|
+
${iconHtml}
|
|
57
|
+
<span class="text-sm font-medium whitespace-pre-wrap break-words inline-block">${msg}</span>
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
container.appendChild(item);
|
|
61
|
+
|
|
62
|
+
// Animate in
|
|
63
|
+
requestAnimationFrame(() => {
|
|
64
|
+
requestAnimationFrame(() => {
|
|
65
|
+
item.classList.remove('scale-95');
|
|
66
|
+
item.classList.add('scale-100');
|
|
67
|
+
item.style.maxHeight = '150px'; // sufficiently large value
|
|
68
|
+
item.style.paddingTop = '';
|
|
69
|
+
item.style.paddingBottom = '';
|
|
70
|
+
item.style.opacity = '1';
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (duration > 0) {
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
this._removeItem(item);
|
|
77
|
+
}, duration);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return () => this._removeItem(item);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_removeItem(item) {
|
|
84
|
+
if (!item.parentNode) return;
|
|
85
|
+
item.classList.remove('scale-100');
|
|
86
|
+
item.classList.add('scale-75');
|
|
87
|
+
item.style.opacity = '0';
|
|
88
|
+
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
if (!item.parentNode) return;
|
|
91
|
+
|
|
92
|
+
// 固定当前高度以便开始过渡动画
|
|
93
|
+
item.style.height = item.offsetHeight + 'px';
|
|
94
|
+
item.style.overflow = 'hidden';
|
|
95
|
+
item.offsetHeight; // 强制重绘
|
|
96
|
+
|
|
97
|
+
// 收缩高度和边距
|
|
98
|
+
item.style.height = '0px';
|
|
99
|
+
item.style.paddingTop = '0px';
|
|
100
|
+
item.style.paddingBottom = '0px';
|
|
101
|
+
item.style.marginTop = '0px';
|
|
102
|
+
item.style.marginBottom = '0px';
|
|
103
|
+
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
if (item.parentNode) item.parentNode.removeChild(item);
|
|
106
|
+
}, 300);
|
|
107
|
+
}, 150);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
hideAll() {
|
|
111
|
+
const container = this.querySelector('.toast-container') || this.firstElementChild;
|
|
112
|
+
if (container) {
|
|
113
|
+
Array.from(container.children).forEach(item => this._removeItem(item));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|