@luanlu/mk-motion 1.1.0 → 1.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/package.json +14 -2
- package/src/a11y/focus-trap.ts +64 -0
- package/src/a11y/keyboard.ts +43 -0
- package/src/components/alert/alert.css +111 -0
- package/src/components/alert/alert.ts +107 -0
- package/src/components/avatar/avatar.css +112 -0
- package/src/components/avatar/avatar.ts +175 -0
- package/src/components/breadcrumb/breadcrumb.css +31 -0
- package/src/components/breadcrumb/breadcrumb.ts +71 -0
- package/src/components/button/button.css +108 -0
- package/src/components/button/button.ts +140 -0
- package/src/components/card/card.css +52 -0
- package/src/components/card/card.ts +87 -0
- package/src/components/collapse/collapse.css +76 -0
- package/src/components/collapse/collapse.ts +168 -0
- package/src/components/dialog/dialog.css +78 -0
- package/src/components/dialog/dialog.ts +164 -0
- package/src/components/drawer/drawer.css +73 -0
- package/src/components/drawer/drawer.ts +131 -0
- package/src/components/empty/empty.css +36 -0
- package/src/components/empty/empty.ts +85 -0
- package/src/components/form/checkbox.css +56 -0
- package/src/components/form/checkbox.ts +119 -0
- package/src/components/form/radio.css +57 -0
- package/src/components/form/radio.ts +153 -0
- package/src/components/form/select.css +91 -0
- package/src/components/form/select.ts +174 -0
- package/src/components/form/slider.css +56 -0
- package/src/components/form/slider.ts +148 -0
- package/src/components/input/input.css +92 -0
- package/src/components/input/input.ts +162 -0
- package/src/components/layout/divider.css +32 -0
- package/src/components/layout/divider.ts +42 -0
- package/src/components/layout/row.css +64 -0
- package/src/components/layout/row.ts +57 -0
- package/src/components/layout/space.css +14 -0
- package/src/components/layout/space.ts +48 -0
- package/src/components/loading/loading.css +37 -0
- package/src/components/loading/loading.ts +46 -0
- package/src/components/menu/menu.css +121 -0
- package/src/components/menu/menu.ts +187 -0
- package/src/components/message/message.css +64 -0
- package/src/components/message/message.ts +96 -0
- package/src/components/popover/popover.css +73 -0
- package/src/components/popover/popover.ts +279 -0
- package/src/components/progress/progress.css +112 -0
- package/src/components/progress/progress.ts +171 -0
- package/src/components/steps/steps.css +127 -0
- package/src/components/steps/steps.ts +102 -0
- package/src/components/styles/components.css +28 -0
- package/src/components/styles/reset.css +24 -0
- package/src/components/styles/tokens.css +248 -0
- package/src/components/styles/variables.css +24 -0
- package/src/components/switch/switch.css +53 -0
- package/src/components/switch/switch.ts +103 -0
- package/src/components/table/table.css +192 -0
- package/src/components/table/table.ts +370 -0
- package/src/components/tabs/tabs.css +138 -0
- package/src/components/tabs/tabs.ts +211 -0
- package/src/components/tag/tag.css +123 -0
- package/src/components/tag/tag.ts +112 -0
- package/src/components/tooltip/tooltip.css +66 -0
- package/src/components/tooltip/tooltip.ts +185 -0
- package/src/core/animator.ts +124 -0
- package/src/core/timeline.ts +128 -0
- package/src/core/utils.ts +47 -0
- package/src/effects/glitch.ts +99 -0
- package/src/effects/particle.ts +134 -0
- package/src/effects/text-split.ts +95 -0
- package/src/effects/wave-text.ts +88 -0
- package/src/gesture/draggable.ts +130 -0
- package/src/gesture/spring.ts +152 -0
- package/src/index.ts +162 -0
- package/src/interactive/coverflow.ts +100 -0
- package/src/interactive/cursor-trail.ts +113 -0
- package/src/interactive/flip-card.ts +114 -0
- package/src/interactive/magnetic.ts +121 -0
- package/src/micro/hover-lift.ts +94 -0
- package/src/micro/ripple.ts +130 -0
- package/src/motion/component-motion.ts +177 -0
- package/src/nuxt/module.ts +46 -0
- package/src/presets/index.ts +69 -0
- package/src/scroll/scroll-trigger.ts +104 -0
- package/src/styles/animations.css +135 -0
- package/src/styles/element-plus.css +174 -0
- package/src/text/count-up.ts +108 -0
- package/src/text/typewriter.ts +109 -0
- package/src/theme/dark.css +19 -0
- package/src/theme/light.css +19 -0
- package/src/theme/theme.ts +65 -0
- package/src/transitions/blur-reveal.ts +92 -0
- package/src/transitions/collapse.ts +112 -0
- package/src/transitions/lazy-image.ts +87 -0
- package/src/transitions/list.ts +75 -0
- package/src/transitions/loading.ts +95 -0
- package/src/transitions/parallax.ts +60 -0
- package/src/transitions/shimmer.ts +105 -0
- package/src/transitions/toast.ts +151 -0
- package/src/types.d.ts +4 -0
- package/src/vite/plugin.ts +45 -0
- package/src/vue/button.ts +28 -9
- package/src/vue/card.ts +28 -8
- package/src/vue/composables/index.ts +4 -0
- package/src/vue/composables/useLoading.ts +12 -0
- package/src/vue/composables/useMessage.ts +16 -0
- package/src/vue/composables/useMotion.ts +19 -0
- package/src/vue/composables/useTheme.ts +12 -0
- package/src/vue/dialog.ts +69 -17
- package/src/vue/index.ts +4 -21
- package/src/vue/input.ts +35 -11
- package/src/vue/slider.ts +22 -4
- package/src/vue/switch.ts +16 -9
- package/src/vue/alert.ts +0 -32
- package/src/vue/avatar.ts +0 -34
- package/src/vue/breadcrumb.ts +0 -32
- package/src/vue/checkbox.ts +0 -32
- package/src/vue/collapse.ts +0 -33
- package/src/vue/divider.ts +0 -32
- package/src/vue/drawer.ts +0 -33
- package/src/vue/empty.ts +0 -33
- package/src/vue/menu.ts +0 -33
- package/src/vue/popover.ts +0 -34
- package/src/vue/progress.ts +0 -33
- package/src/vue/row.ts +0 -32
- package/src/vue/select.ts +0 -33
- package/src/vue/space.ts +0 -32
- package/src/vue/steps.ts +0 -33
- package/src/vue/table.ts +0 -33
- package/src/vue/tabs.ts +0 -33
- package/src/vue/tag.ts +0 -33
- package/src/vue/tooltip.ts +0 -34
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import './slider.css'
|
|
2
|
+
import { onKey, Keys } from '../../a11y/keyboard.ts'
|
|
3
|
+
|
|
4
|
+
export interface SliderOptions {
|
|
5
|
+
min?: number
|
|
6
|
+
max?: number
|
|
7
|
+
step?: number
|
|
8
|
+
value?: number
|
|
9
|
+
showValue?: boolean
|
|
10
|
+
onChange?: (value: number) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class MkSlider {
|
|
14
|
+
el: HTMLDivElement
|
|
15
|
+
private track: HTMLDivElement
|
|
16
|
+
private fill: HTMLDivElement
|
|
17
|
+
private thumb: HTMLDivElement
|
|
18
|
+
private valueEl?: HTMLDivElement
|
|
19
|
+
private options: Required<Omit<SliderOptions, 'onChange'>> & Pick<SliderOptions, 'onChange'>
|
|
20
|
+
private _value: number
|
|
21
|
+
private dragging = false
|
|
22
|
+
private _cleanupKey?: () => void
|
|
23
|
+
|
|
24
|
+
constructor(container: HTMLElement | string, options: SliderOptions = {}) {
|
|
25
|
+
const parent =
|
|
26
|
+
typeof container === 'string'
|
|
27
|
+
? document.querySelector(container)!
|
|
28
|
+
: container
|
|
29
|
+
|
|
30
|
+
this.options = {
|
|
31
|
+
min: 0,
|
|
32
|
+
max: 100,
|
|
33
|
+
step: 1,
|
|
34
|
+
value: 0,
|
|
35
|
+
showValue: true,
|
|
36
|
+
...options,
|
|
37
|
+
}
|
|
38
|
+
this._value = this.options.value
|
|
39
|
+
|
|
40
|
+
this.el = document.createElement('div')
|
|
41
|
+
this.el.className = 'mk-slider'
|
|
42
|
+
|
|
43
|
+
this.track = document.createElement('div')
|
|
44
|
+
this.track.className = 'mk-slider__track'
|
|
45
|
+
|
|
46
|
+
this.fill = document.createElement('div')
|
|
47
|
+
this.fill.className = 'mk-slider__fill'
|
|
48
|
+
this.track.appendChild(this.fill)
|
|
49
|
+
|
|
50
|
+
this.thumb = document.createElement('div')
|
|
51
|
+
this.thumb.className = 'mk-slider__thumb'
|
|
52
|
+
this.thumb.setAttribute('role', 'slider')
|
|
53
|
+
this.thumb.setAttribute('tabindex', '0')
|
|
54
|
+
this.track.appendChild(this.thumb)
|
|
55
|
+
|
|
56
|
+
this.el.appendChild(this.track)
|
|
57
|
+
|
|
58
|
+
if (this.options.showValue) {
|
|
59
|
+
this.valueEl = document.createElement('div')
|
|
60
|
+
this.valueEl.className = 'mk-slider__value'
|
|
61
|
+
this.el.appendChild(this.valueEl)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.updateUI()
|
|
65
|
+
|
|
66
|
+
this._cleanupKey = onKey(this.thumb, [
|
|
67
|
+
{ key: Keys.ArrowLeft, handler: () => this.adjustValue(-this.options.step) },
|
|
68
|
+
{ key: Keys.ArrowRight, handler: () => this.adjustValue(this.options.step) },
|
|
69
|
+
{ key: Keys.Home, handler: () => { this.value = this.options.min } },
|
|
70
|
+
{ key: Keys.End, handler: () => { this.value = this.options.max } },
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
this.track.addEventListener('mousedown', (e) => this.onStart(e.clientX))
|
|
74
|
+
this.thumb.addEventListener('mousedown', (e) => {
|
|
75
|
+
e.stopPropagation()
|
|
76
|
+
this.onStart(e.clientX)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
document.addEventListener('mousemove', (e) => this.onMove(e.clientX))
|
|
80
|
+
document.addEventListener('mouseup', () => this.onEnd())
|
|
81
|
+
|
|
82
|
+
// Touch
|
|
83
|
+
this.track.addEventListener('touchstart', (e) => this.onStart(e.touches[0].clientX), { passive: true })
|
|
84
|
+
document.addEventListener('touchmove', (e) => this.onMove(e.touches[0].clientX), { passive: true })
|
|
85
|
+
document.addEventListener('touchend', () => this.onEnd())
|
|
86
|
+
|
|
87
|
+
parent.appendChild(this.el)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private onStart(clientX: number): void {
|
|
91
|
+
this.dragging = true
|
|
92
|
+
this.updateFromPosition(clientX)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private onMove(clientX: number): void {
|
|
96
|
+
if (!this.dragging) return
|
|
97
|
+
this.updateFromPosition(clientX)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private onEnd(): void {
|
|
101
|
+
this.dragging = false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private updateFromPosition(clientX: number): void {
|
|
105
|
+
const rect = this.track.getBoundingClientRect()
|
|
106
|
+
const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))
|
|
107
|
+
const raw = this.options.min + percent * (this.options.max - this.options.min)
|
|
108
|
+
const stepped = Math.round(raw / this.options.step) * this.options.step
|
|
109
|
+
this.value = Math.max(this.options.min, Math.min(this.options.max, stepped))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get value(): number {
|
|
113
|
+
return this._value
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
set value(v: number) {
|
|
117
|
+
if (this._value !== v) {
|
|
118
|
+
this._value = v
|
|
119
|
+
this.updateUI()
|
|
120
|
+
this.options.onChange?.(v)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private updateUI(): void {
|
|
125
|
+
const percent = (this._value - this.options.min) / (this.options.max - this.options.min)
|
|
126
|
+
this.fill.style.width = `${percent * 100}%`
|
|
127
|
+
this.thumb.style.left = `${percent * 100}%`
|
|
128
|
+
if (this.valueEl) {
|
|
129
|
+
this.valueEl.textContent = String(this._value)
|
|
130
|
+
}
|
|
131
|
+
this.updateAria()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private updateAria(): void {
|
|
135
|
+
this.thumb.setAttribute('aria-valuenow', String(this._value))
|
|
136
|
+
this.thumb.setAttribute('aria-valuemin', String(this.options.min))
|
|
137
|
+
this.thumb.setAttribute('aria-valuemax', String(this.options.max))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private adjustValue(delta: number): void {
|
|
141
|
+
this.value = Math.max(this.options.min, Math.min(this.options.max, this._value + delta))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
destroy(): void {
|
|
145
|
+
this._cleanupKey?.()
|
|
146
|
+
this.el.remove()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
.mk-input-wrapper {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
width: 100%;
|
|
5
|
+
max-width: 320px;
|
|
6
|
+
align-items: center;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.mk-input {
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 36px;
|
|
12
|
+
padding: 0 12px;
|
|
13
|
+
font-size: 13px;
|
|
14
|
+
color: var(--mk-text);
|
|
15
|
+
background: var(--mk-surface);
|
|
16
|
+
border: 1px solid var(--mk-border);
|
|
17
|
+
border-radius: var(--mk-radius);
|
|
18
|
+
outline: none;
|
|
19
|
+
transition: var(--mk-transition);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.mk-input::placeholder { color: var(--mk-text-tertiary); }
|
|
23
|
+
|
|
24
|
+
.mk-input:hover {
|
|
25
|
+
border-color: var(--mk-border-hover);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.mk-input:focus {
|
|
29
|
+
border-color: var(--mk-primary);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Error */
|
|
33
|
+
.mk-input-wrapper.is-error .mk-input {
|
|
34
|
+
border-color: var(--mk-danger);
|
|
35
|
+
animation: mk-input-shake 0.3s ease;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.mk-input-wrapper.is-success .mk-input {
|
|
39
|
+
border-color: var(--mk-success);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.mk-input__suffix {
|
|
43
|
+
position: absolute;
|
|
44
|
+
right: 10px;
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: 6px;
|
|
48
|
+
color: var(--mk-text-tertiary);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.mk-input__suffix-item {
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
font-size: 13px;
|
|
54
|
+
width: 20px; height: 20px;
|
|
55
|
+
display: flex; align-items: center; justify-content: center;
|
|
56
|
+
border-radius: 4px;
|
|
57
|
+
transition: var(--mk-transition);
|
|
58
|
+
}
|
|
59
|
+
.mk-input__suffix-item:hover {
|
|
60
|
+
color: var(--mk-text);
|
|
61
|
+
background: var(--mk-surface-hover);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.mk-input__clear {
|
|
65
|
+
opacity: 0;
|
|
66
|
+
transition: opacity 0.2s;
|
|
67
|
+
}
|
|
68
|
+
.mk-input-wrapper:hover .mk-input__clear,
|
|
69
|
+
.mk-input:focus ~ .mk-input__suffix .mk-input__clear {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.mk-input__errormsg {
|
|
74
|
+
position: absolute;
|
|
75
|
+
bottom: -20px;
|
|
76
|
+
left: 0;
|
|
77
|
+
font-size: 12px;
|
|
78
|
+
color: var(--mk-danger);
|
|
79
|
+
opacity: 0;
|
|
80
|
+
transform: translateY(-2px);
|
|
81
|
+
transition: var(--mk-transition);
|
|
82
|
+
}
|
|
83
|
+
.mk-input__errormsg.show {
|
|
84
|
+
opacity: 1;
|
|
85
|
+
transform: translateY(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@keyframes mk-input-shake {
|
|
89
|
+
0%, 100% { transform: translateX(0); }
|
|
90
|
+
25% { transform: translateX(-4px); }
|
|
91
|
+
75% { transform: translateX(4px); }
|
|
92
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import './input.css'
|
|
2
|
+
import { withMotion, type MotionOptions } from '../../motion/component-motion.ts'
|
|
3
|
+
|
|
4
|
+
export interface InputOptions {
|
|
5
|
+
placeholder?: string
|
|
6
|
+
type?: string
|
|
7
|
+
value?: string
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
clearable?: boolean
|
|
10
|
+
showPassword?: boolean
|
|
11
|
+
validate?: (value: string) => string | null
|
|
12
|
+
onInput?: (value: string) => void
|
|
13
|
+
onEnter?: (value: string) => void
|
|
14
|
+
motion?: MotionOptions
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class MkInput {
|
|
18
|
+
el: HTMLDivElement
|
|
19
|
+
input: HTMLInputElement
|
|
20
|
+
private options: InputOptions
|
|
21
|
+
private errorMsg: HTMLSpanElement
|
|
22
|
+
private motion: ReturnType<typeof withMotion> | null = null
|
|
23
|
+
|
|
24
|
+
constructor(container: HTMLElement | string, options: InputOptions = {}) {
|
|
25
|
+
const parent =
|
|
26
|
+
typeof container === 'string'
|
|
27
|
+
? document.querySelector(container)!
|
|
28
|
+
: container
|
|
29
|
+
|
|
30
|
+
this.options = { ...options }
|
|
31
|
+
|
|
32
|
+
this.el = document.createElement('div')
|
|
33
|
+
this.el.className = 'mk-input-wrapper'
|
|
34
|
+
|
|
35
|
+
const inputId = `mk-input-${Math.random().toString(36).slice(2)}`
|
|
36
|
+
|
|
37
|
+
this.input = document.createElement('input')
|
|
38
|
+
this.input.className = 'mk-input'
|
|
39
|
+
this.input.id = inputId
|
|
40
|
+
this.input.type = this.options.type || 'text'
|
|
41
|
+
this.input.placeholder = this.options.placeholder || ''
|
|
42
|
+
this.input.value = this.options.value || ''
|
|
43
|
+
this.input.disabled = !!this.options.disabled
|
|
44
|
+
|
|
45
|
+
this.el.appendChild(this.input)
|
|
46
|
+
|
|
47
|
+
const suffix = document.createElement('span')
|
|
48
|
+
suffix.className = 'mk-input__suffix'
|
|
49
|
+
|
|
50
|
+
if (this.options.clearable) {
|
|
51
|
+
const clear = document.createElement('span')
|
|
52
|
+
clear.className = 'mk-input__suffix-item mk-input__clear'
|
|
53
|
+
clear.innerHTML = '✕'
|
|
54
|
+
clear.addEventListener('click', () => {
|
|
55
|
+
this.input.value = ''
|
|
56
|
+
this.input.focus()
|
|
57
|
+
this.options.onInput?.('')
|
|
58
|
+
this.clearError()
|
|
59
|
+
})
|
|
60
|
+
suffix.appendChild(clear)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.options.showPassword && this.input.type === 'password') {
|
|
64
|
+
const eye = document.createElement('span')
|
|
65
|
+
eye.className = 'mk-input__suffix-item'
|
|
66
|
+
eye.innerHTML = '👁'
|
|
67
|
+
eye.title = '显示密码'
|
|
68
|
+
let showing = false
|
|
69
|
+
eye.addEventListener('click', () => {
|
|
70
|
+
showing = !showing
|
|
71
|
+
this.input.type = showing ? 'text' : 'password'
|
|
72
|
+
eye.innerHTML = showing ? '🙈' : '👁'
|
|
73
|
+
})
|
|
74
|
+
suffix.appendChild(eye)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (suffix.children.length > 0) {
|
|
78
|
+
this.el.appendChild(suffix)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.errorMsg = document.createElement('span')
|
|
82
|
+
this.errorMsg.className = 'mk-input__errormsg'
|
|
83
|
+
this.errorMsg.id = `${inputId}-error`
|
|
84
|
+
this.el.appendChild(this.errorMsg)
|
|
85
|
+
|
|
86
|
+
this.input.addEventListener('input', () => {
|
|
87
|
+
this.options.onInput?.(this.input.value)
|
|
88
|
+
this.clearError()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
this.input.addEventListener('keydown', (e) => {
|
|
92
|
+
if (e.key === 'Enter') {
|
|
93
|
+
this.options.onEnter?.(this.input.value)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
this.input.addEventListener('blur', () => {
|
|
98
|
+
this.validate()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
parent.appendChild(this.el)
|
|
102
|
+
|
|
103
|
+
this.motion = withMotion(this.input, options.motion || { focus: 'ring', enter: 'fadeIn', duration: 200 })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get value(): string {
|
|
107
|
+
return this.input.value
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
set value(v: string) {
|
|
111
|
+
this.input.value = v
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
validate(): boolean {
|
|
115
|
+
if (!this.options.validate) return true
|
|
116
|
+
const error = this.options.validate(this.input.value)
|
|
117
|
+
if (error) {
|
|
118
|
+
this.showError(error)
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
this.clearError()
|
|
122
|
+
return true
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
showError(msg: string): void {
|
|
126
|
+
this.el.classList.add('is-error')
|
|
127
|
+
this.el.classList.remove('is-success')
|
|
128
|
+
this.errorMsg.textContent = msg
|
|
129
|
+
this.errorMsg.classList.add('show')
|
|
130
|
+
this.input.setAttribute('aria-invalid', 'true')
|
|
131
|
+
this.input.setAttribute('aria-describedby', this.errorMsg.id)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
showSuccess(): void {
|
|
135
|
+
this.el.classList.remove('is-error')
|
|
136
|
+
this.el.classList.add('is-success')
|
|
137
|
+
this.errorMsg.classList.remove('show')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
clearError(): void {
|
|
141
|
+
this.el.classList.remove('is-error')
|
|
142
|
+
this.errorMsg.classList.remove('show')
|
|
143
|
+
this.input.removeAttribute('aria-invalid')
|
|
144
|
+
this.input.removeAttribute('aria-describedby')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
focus(): void {
|
|
148
|
+
this.input.focus()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
destroy(): void {
|
|
152
|
+
this.motion?.destroy()
|
|
153
|
+
this.el.remove()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function createInput(
|
|
158
|
+
container: HTMLElement | string,
|
|
159
|
+
options?: InputOptions
|
|
160
|
+
): MkInput {
|
|
161
|
+
return new MkInput(container, options)
|
|
162
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.mk-divider {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
color: var(--mk-text-tertiary);
|
|
5
|
+
font-size: 13px;
|
|
6
|
+
margin: 16px 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.mk-divider::before,
|
|
10
|
+
.mk-divider::after {
|
|
11
|
+
content: '';
|
|
12
|
+
flex: 1;
|
|
13
|
+
height: 1px;
|
|
14
|
+
background: var(--mk-border);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.mk-divider--horizontal::before { margin-right: 12px; }
|
|
18
|
+
.mk-divider--horizontal::after { margin-left: 12px; }
|
|
19
|
+
|
|
20
|
+
.mk-divider--vertical {
|
|
21
|
+
display: inline-block;
|
|
22
|
+
width: 1px;
|
|
23
|
+
height: 1em;
|
|
24
|
+
background: var(--mk-border);
|
|
25
|
+
margin: 0 8px;
|
|
26
|
+
vertical-align: middle;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.mk-divider--dashed::before,
|
|
30
|
+
.mk-divider--dashed::after {
|
|
31
|
+
background: repeating-linear-gradient(90deg, var(--mk-border), var(--mk-border) 4px, transparent 4px, transparent 8px);
|
|
32
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import './divider.css'
|
|
2
|
+
|
|
3
|
+
export interface DividerOptions {
|
|
4
|
+
text?: string
|
|
5
|
+
direction?: 'horizontal' | 'vertical'
|
|
6
|
+
dashed?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class MkDivider {
|
|
10
|
+
el: HTMLDivElement
|
|
11
|
+
|
|
12
|
+
constructor(container: HTMLElement | string, options: DividerOptions = {}) {
|
|
13
|
+
const parent =
|
|
14
|
+
typeof container === 'string'
|
|
15
|
+
? document.querySelector(container)!
|
|
16
|
+
: container
|
|
17
|
+
|
|
18
|
+
this.el = document.createElement('div')
|
|
19
|
+
this.el.className = 'mk-divider'
|
|
20
|
+
|
|
21
|
+
if (options.direction === 'vertical') {
|
|
22
|
+
this.el.classList.add('mk-divider--vertical')
|
|
23
|
+
} else {
|
|
24
|
+
this.el.classList.add('mk-divider--horizontal')
|
|
25
|
+
}
|
|
26
|
+
if (options.dashed) this.el.classList.add('mk-divider--dashed')
|
|
27
|
+
if (options.text) this.el.textContent = options.text
|
|
28
|
+
|
|
29
|
+
parent.appendChild(this.el)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
destroy(): void {
|
|
33
|
+
this.el.remove()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createDivider(
|
|
38
|
+
container: HTMLElement | string,
|
|
39
|
+
options?: DividerOptions
|
|
40
|
+
): MkDivider {
|
|
41
|
+
return new MkDivider(container, options)
|
|
42
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.mk-row {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
margin: 0 -8px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.mk-row--no-wrap { flex-wrap: nowrap; }
|
|
8
|
+
.mk-row--justify-start { justify-content: flex-start; }
|
|
9
|
+
.mk-row--justify-center { justify-content: center; }
|
|
10
|
+
.mk-row--justify-end { justify-content: flex-end; }
|
|
11
|
+
.mk-row--justify-space-between { justify-content: space-between; }
|
|
12
|
+
.mk-row--justify-space-around { justify-content: space-around; }
|
|
13
|
+
.mk-row--align-top { align-items: flex-start; }
|
|
14
|
+
.mk-row--align-middle { align-items: center; }
|
|
15
|
+
.mk-row--align-bottom { align-items: flex-end; }
|
|
16
|
+
|
|
17
|
+
.mk-col {
|
|
18
|
+
padding: 0 8px;
|
|
19
|
+
flex: 0 0 auto;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.mk-col--flex { flex: 1; }
|
|
23
|
+
|
|
24
|
+
/* 24-column grid */
|
|
25
|
+
.mk-col-1 { flex: 0 0 4.1667%; }
|
|
26
|
+
.mk-col-2 { flex: 0 0 8.3333%; }
|
|
27
|
+
.mk-col-3 { flex: 0 0 12.5%; }
|
|
28
|
+
.mk-col-4 { flex: 0 0 16.6667%; }
|
|
29
|
+
.mk-col-5 { flex: 0 0 20.8333%; }
|
|
30
|
+
.mk-col-6 { flex: 0 0 25%; }
|
|
31
|
+
.mk-col-7 { flex: 0 0 29.1667%; }
|
|
32
|
+
.mk-col-8 { flex: 0 0 33.3333%; }
|
|
33
|
+
.mk-col-9 { flex: 0 0 37.5%; }
|
|
34
|
+
.mk-col-10 { flex: 0 0 41.6667%; }
|
|
35
|
+
.mk-col-11 { flex: 0 0 45.8333%; }
|
|
36
|
+
.mk-col-12 { flex: 0 0 50%; }
|
|
37
|
+
.mk-col-13 { flex: 0 0 54.1667%; }
|
|
38
|
+
.mk-col-14 { flex: 0 0 58.3333%; }
|
|
39
|
+
.mk-col-15 { flex: 0 0 62.5%; }
|
|
40
|
+
.mk-col-16 { flex: 0 0 66.6667%; }
|
|
41
|
+
.mk-col-17 { flex: 0 0 70.8333%; }
|
|
42
|
+
.mk-col-18 { flex: 0 0 75%; }
|
|
43
|
+
.mk-col-19 { flex: 0 0 79.1667%; }
|
|
44
|
+
.mk-col-20 { flex: 0 0 83.3333%; }
|
|
45
|
+
.mk-col-21 { flex: 0 0 87.5%; }
|
|
46
|
+
.mk-col-22 { flex: 0 0 91.6667%; }
|
|
47
|
+
.mk-col-23 { flex: 0 0 95.8333%; }
|
|
48
|
+
.mk-col-24 { flex: 0 0 100%; }
|
|
49
|
+
|
|
50
|
+
/* Responsive - md (768px) */
|
|
51
|
+
@media (min-width: 768px) {
|
|
52
|
+
.mk-col-md-6 { flex: 0 0 25%; }
|
|
53
|
+
.mk-col-md-8 { flex: 0 0 33.3333%; }
|
|
54
|
+
.mk-col-md-12 { flex: 0 0 50%; }
|
|
55
|
+
.mk-col-md-24 { flex: 0 0 100%; }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Responsive - lg (1024px) */
|
|
59
|
+
@media (min-width: 1024px) {
|
|
60
|
+
.mk-col-lg-6 { flex: 0 0 25%; }
|
|
61
|
+
.mk-col-lg-8 { flex: 0 0 33.3333%; }
|
|
62
|
+
.mk-col-lg-12 { flex: 0 0 50%; }
|
|
63
|
+
.mk-col-lg-24 { flex: 0 0 100%; }
|
|
64
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import './row.css'
|
|
2
|
+
|
|
3
|
+
export interface RowOptions {
|
|
4
|
+
gutter?: number
|
|
5
|
+
justify?: 'start' | 'center' | 'end' | 'space-between' | 'space-around'
|
|
6
|
+
align?: 'top' | 'middle' | 'bottom'
|
|
7
|
+
wrap?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class MkRow {
|
|
11
|
+
el: HTMLDivElement
|
|
12
|
+
|
|
13
|
+
constructor(container: HTMLElement | string, options: RowOptions = {}) {
|
|
14
|
+
const parent =
|
|
15
|
+
typeof container === 'string'
|
|
16
|
+
? document.querySelector(container)!
|
|
17
|
+
: container
|
|
18
|
+
|
|
19
|
+
this.el = document.createElement('div')
|
|
20
|
+
this.el.className = 'mk-row'
|
|
21
|
+
|
|
22
|
+
if (options.justify) this.el.classList.add(`mk-row--justify-${options.justify}`)
|
|
23
|
+
if (options.align) this.el.classList.add(`mk-row--align-${options.align}`)
|
|
24
|
+
if (options.wrap === false) this.el.classList.add('mk-row--no-wrap')
|
|
25
|
+
if (options.gutter) {
|
|
26
|
+
this.el.style.margin = `0 -${options.gutter / 2}px`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
parent.appendChild(this.el)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
addCol(span: number | 'flex', content?: string | HTMLElement, className?: string): HTMLDivElement {
|
|
33
|
+
const col = document.createElement('div')
|
|
34
|
+
col.className = `mk-col mk-col-${span === 'flex' ? 'flex' : span}`
|
|
35
|
+
if (className) col.classList.add(className)
|
|
36
|
+
|
|
37
|
+
if (typeof content === 'string') {
|
|
38
|
+
col.innerHTML = content
|
|
39
|
+
} else if (content) {
|
|
40
|
+
col.appendChild(content)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.el.appendChild(col)
|
|
44
|
+
return col
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
destroy(): void {
|
|
48
|
+
this.el.remove()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createRow(
|
|
53
|
+
container: HTMLElement | string,
|
|
54
|
+
options?: RowOptions
|
|
55
|
+
): MkRow {
|
|
56
|
+
return new MkRow(container, options)
|
|
57
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.mk-space {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
flex-wrap: wrap;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.mk-space--vertical { flex-direction: column; }
|
|
8
|
+
|
|
9
|
+
.mk-space--small { gap: 4px; }
|
|
10
|
+
.mk-space--default { gap: 8px; }
|
|
11
|
+
.mk-space--large { gap: 16px; }
|
|
12
|
+
|
|
13
|
+
.mk-space--wrap { flex-wrap: wrap; }
|
|
14
|
+
.mk-space--nowrap { flex-wrap: nowrap; }
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import './space.css'
|
|
2
|
+
|
|
3
|
+
export interface SpaceOptions {
|
|
4
|
+
direction?: 'horizontal' | 'vertical'
|
|
5
|
+
size?: 'small' | 'default' | 'large'
|
|
6
|
+
wrap?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class MkSpace {
|
|
10
|
+
el: HTMLDivElement
|
|
11
|
+
|
|
12
|
+
constructor(container: HTMLElement | string, options: SpaceOptions = {}) {
|
|
13
|
+
const parent =
|
|
14
|
+
typeof container === 'string'
|
|
15
|
+
? document.querySelector(container)!
|
|
16
|
+
: container
|
|
17
|
+
|
|
18
|
+
this.el = document.createElement('div')
|
|
19
|
+
this.el.className = 'mk-space'
|
|
20
|
+
|
|
21
|
+
if (options.direction === 'vertical') this.el.classList.add('mk-space--vertical')
|
|
22
|
+
if (options.size) this.el.classList.add(`mk-space--${options.size}`)
|
|
23
|
+
if (options.wrap !== false) this.el.classList.add('mk-space--wrap')
|
|
24
|
+
|
|
25
|
+
parent.appendChild(this.el)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
add(content: string | HTMLElement): void {
|
|
29
|
+
if (typeof content === 'string') {
|
|
30
|
+
const span = document.createElement('span')
|
|
31
|
+
span.innerHTML = content
|
|
32
|
+
this.el.appendChild(span)
|
|
33
|
+
} else {
|
|
34
|
+
this.el.appendChild(content)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
destroy(): void {
|
|
39
|
+
this.el.remove()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createSpace(
|
|
44
|
+
container: HTMLElement | string,
|
|
45
|
+
options?: SpaceOptions
|
|
46
|
+
): MkSpace {
|
|
47
|
+
return new MkSpace(container, options)
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.mk-loading-mask {
|
|
2
|
+
position: absolute;
|
|
3
|
+
inset: 0;
|
|
4
|
+
background: rgba(9, 9, 11, 0.8);
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
z-index: 100;
|
|
9
|
+
border-radius: inherit;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 10px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.mk-loading-mask.is-fullscreen {
|
|
15
|
+
position: fixed;
|
|
16
|
+
background: rgba(9, 9, 11, 0.85);
|
|
17
|
+
z-index: 9999;
|
|
18
|
+
border-radius: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.mk-loading__spinner {
|
|
22
|
+
width: 32px;
|
|
23
|
+
height: 32px;
|
|
24
|
+
border: 2px solid var(--mk-border);
|
|
25
|
+
border-top-color: var(--mk-primary);
|
|
26
|
+
border-radius: 50%;
|
|
27
|
+
animation: mk-spin 0.7s linear infinite;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.mk-loading__text {
|
|
31
|
+
font-size: 13px;
|
|
32
|
+
color: var(--mk-text-secondary);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@keyframes mk-spin {
|
|
36
|
+
to { transform: rotate(360deg); }
|
|
37
|
+
}
|