@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.
Files changed (131) hide show
  1. package/package.json +14 -2
  2. package/src/a11y/focus-trap.ts +64 -0
  3. package/src/a11y/keyboard.ts +43 -0
  4. package/src/components/alert/alert.css +111 -0
  5. package/src/components/alert/alert.ts +107 -0
  6. package/src/components/avatar/avatar.css +112 -0
  7. package/src/components/avatar/avatar.ts +175 -0
  8. package/src/components/breadcrumb/breadcrumb.css +31 -0
  9. package/src/components/breadcrumb/breadcrumb.ts +71 -0
  10. package/src/components/button/button.css +108 -0
  11. package/src/components/button/button.ts +140 -0
  12. package/src/components/card/card.css +52 -0
  13. package/src/components/card/card.ts +87 -0
  14. package/src/components/collapse/collapse.css +76 -0
  15. package/src/components/collapse/collapse.ts +168 -0
  16. package/src/components/dialog/dialog.css +78 -0
  17. package/src/components/dialog/dialog.ts +164 -0
  18. package/src/components/drawer/drawer.css +73 -0
  19. package/src/components/drawer/drawer.ts +131 -0
  20. package/src/components/empty/empty.css +36 -0
  21. package/src/components/empty/empty.ts +85 -0
  22. package/src/components/form/checkbox.css +56 -0
  23. package/src/components/form/checkbox.ts +119 -0
  24. package/src/components/form/radio.css +57 -0
  25. package/src/components/form/radio.ts +153 -0
  26. package/src/components/form/select.css +91 -0
  27. package/src/components/form/select.ts +174 -0
  28. package/src/components/form/slider.css +56 -0
  29. package/src/components/form/slider.ts +148 -0
  30. package/src/components/input/input.css +92 -0
  31. package/src/components/input/input.ts +162 -0
  32. package/src/components/layout/divider.css +32 -0
  33. package/src/components/layout/divider.ts +42 -0
  34. package/src/components/layout/row.css +64 -0
  35. package/src/components/layout/row.ts +57 -0
  36. package/src/components/layout/space.css +14 -0
  37. package/src/components/layout/space.ts +48 -0
  38. package/src/components/loading/loading.css +37 -0
  39. package/src/components/loading/loading.ts +46 -0
  40. package/src/components/menu/menu.css +121 -0
  41. package/src/components/menu/menu.ts +187 -0
  42. package/src/components/message/message.css +64 -0
  43. package/src/components/message/message.ts +96 -0
  44. package/src/components/popover/popover.css +73 -0
  45. package/src/components/popover/popover.ts +279 -0
  46. package/src/components/progress/progress.css +112 -0
  47. package/src/components/progress/progress.ts +171 -0
  48. package/src/components/steps/steps.css +127 -0
  49. package/src/components/steps/steps.ts +102 -0
  50. package/src/components/styles/components.css +28 -0
  51. package/src/components/styles/reset.css +24 -0
  52. package/src/components/styles/tokens.css +248 -0
  53. package/src/components/styles/variables.css +24 -0
  54. package/src/components/switch/switch.css +53 -0
  55. package/src/components/switch/switch.ts +103 -0
  56. package/src/components/table/table.css +192 -0
  57. package/src/components/table/table.ts +370 -0
  58. package/src/components/tabs/tabs.css +138 -0
  59. package/src/components/tabs/tabs.ts +211 -0
  60. package/src/components/tag/tag.css +123 -0
  61. package/src/components/tag/tag.ts +112 -0
  62. package/src/components/tooltip/tooltip.css +66 -0
  63. package/src/components/tooltip/tooltip.ts +185 -0
  64. package/src/core/animator.ts +124 -0
  65. package/src/core/timeline.ts +128 -0
  66. package/src/core/utils.ts +47 -0
  67. package/src/effects/glitch.ts +99 -0
  68. package/src/effects/particle.ts +134 -0
  69. package/src/effects/text-split.ts +95 -0
  70. package/src/effects/wave-text.ts +88 -0
  71. package/src/gesture/draggable.ts +130 -0
  72. package/src/gesture/spring.ts +152 -0
  73. package/src/index.ts +162 -0
  74. package/src/interactive/coverflow.ts +100 -0
  75. package/src/interactive/cursor-trail.ts +113 -0
  76. package/src/interactive/flip-card.ts +114 -0
  77. package/src/interactive/magnetic.ts +121 -0
  78. package/src/micro/hover-lift.ts +94 -0
  79. package/src/micro/ripple.ts +130 -0
  80. package/src/motion/component-motion.ts +177 -0
  81. package/src/nuxt/module.ts +46 -0
  82. package/src/presets/index.ts +69 -0
  83. package/src/scroll/scroll-trigger.ts +104 -0
  84. package/src/styles/animations.css +135 -0
  85. package/src/styles/element-plus.css +174 -0
  86. package/src/text/count-up.ts +108 -0
  87. package/src/text/typewriter.ts +109 -0
  88. package/src/theme/dark.css +19 -0
  89. package/src/theme/light.css +19 -0
  90. package/src/theme/theme.ts +65 -0
  91. package/src/transitions/blur-reveal.ts +92 -0
  92. package/src/transitions/collapse.ts +112 -0
  93. package/src/transitions/lazy-image.ts +87 -0
  94. package/src/transitions/list.ts +75 -0
  95. package/src/transitions/loading.ts +95 -0
  96. package/src/transitions/parallax.ts +60 -0
  97. package/src/transitions/shimmer.ts +105 -0
  98. package/src/transitions/toast.ts +151 -0
  99. package/src/types.d.ts +4 -0
  100. package/src/vite/plugin.ts +45 -0
  101. package/src/vue/button.ts +28 -9
  102. package/src/vue/card.ts +28 -8
  103. package/src/vue/composables/index.ts +4 -0
  104. package/src/vue/composables/useLoading.ts +12 -0
  105. package/src/vue/composables/useMessage.ts +16 -0
  106. package/src/vue/composables/useMotion.ts +19 -0
  107. package/src/vue/composables/useTheme.ts +12 -0
  108. package/src/vue/dialog.ts +69 -17
  109. package/src/vue/index.ts +4 -21
  110. package/src/vue/input.ts +35 -11
  111. package/src/vue/slider.ts +22 -4
  112. package/src/vue/switch.ts +16 -9
  113. package/src/vue/alert.ts +0 -32
  114. package/src/vue/avatar.ts +0 -34
  115. package/src/vue/breadcrumb.ts +0 -32
  116. package/src/vue/checkbox.ts +0 -32
  117. package/src/vue/collapse.ts +0 -33
  118. package/src/vue/divider.ts +0 -32
  119. package/src/vue/drawer.ts +0 -33
  120. package/src/vue/empty.ts +0 -33
  121. package/src/vue/menu.ts +0 -33
  122. package/src/vue/popover.ts +0 -34
  123. package/src/vue/progress.ts +0 -33
  124. package/src/vue/row.ts +0 -32
  125. package/src/vue/select.ts +0 -33
  126. package/src/vue/space.ts +0 -32
  127. package/src/vue/steps.ts +0 -33
  128. package/src/vue/table.ts +0 -33
  129. package/src/vue/tabs.ts +0 -33
  130. package/src/vue/tag.ts +0 -33
  131. package/src/vue/tooltip.ts +0 -34
@@ -0,0 +1,279 @@
1
+ import '../../styles/element-plus.css'
2
+ import './popover.css'
3
+ import { FocusTrap } from '../../a11y/focus-trap'
4
+ import { onKey, Keys } from '../../a11y/keyboard'
5
+
6
+ export interface PopoverOptions {
7
+ content?: string | HTMLElement
8
+ title?: string
9
+ placement?: 'top' | 'bottom' | 'left' | 'right'
10
+ trigger?: 'click' | 'hover'
11
+ width?: number
12
+ offset?: number
13
+ }
14
+
15
+ export class MkPopover {
16
+ el: HTMLDivElement
17
+ private options: PopoverOptions
18
+ private popoverEl: HTMLDivElement | null = null
19
+ private arrowEl: HTMLDivElement | null = null
20
+ private contentEl: HTMLDivElement | null = null
21
+ private showTimer: ReturnType<typeof setTimeout> | null = null
22
+ private hideTimer: ReturnType<typeof setTimeout> | null = null
23
+ private isVisible = false
24
+ private cleanupFns: Array<() => void> = []
25
+ private focusTrap: FocusTrap | null = null
26
+ private escapeCleanup: (() => void) | null = null
27
+ constructor(target: HTMLElement, options: PopoverOptions = {}) {
28
+ this.options = {
29
+ placement: 'top',
30
+ trigger: 'hover',
31
+ offset: 8,
32
+ ...options,
33
+ }
34
+
35
+ this.el = document.createElement('div')
36
+ this.el.style.display = 'contents'
37
+
38
+ this.buildPopover()
39
+
40
+ if (this.options.trigger === 'click') {
41
+ const onClick = (e: MouseEvent) => {
42
+ e.stopPropagation()
43
+ this.toggle()
44
+ }
45
+ target.addEventListener('click', onClick)
46
+ this.cleanupFns.push(() => target.removeEventListener('click', onClick))
47
+
48
+ const onDocClick = (e: MouseEvent) => {
49
+ if (
50
+ this.isVisible &&
51
+ this.popoverEl &&
52
+ !this.popoverEl.contains(e.target as Node) &&
53
+ e.target !== target
54
+ ) {
55
+ this.hide()
56
+ }
57
+ }
58
+ document.addEventListener('click', onDocClick)
59
+ this.cleanupFns.push(() => document.removeEventListener('click', onDocClick))
60
+ } else {
61
+ const onEnter = () => {
62
+ if (this.hideTimer) {
63
+ clearTimeout(this.hideTimer)
64
+ this.hideTimer = null
65
+ }
66
+ this.showTimer = setTimeout(() => this.show(), 150)
67
+ }
68
+ const onLeave = () => {
69
+ if (this.showTimer) {
70
+ clearTimeout(this.showTimer)
71
+ this.showTimer = null
72
+ }
73
+ this.hideTimer = setTimeout(() => this.hide(), 150)
74
+ }
75
+
76
+ target.addEventListener('mouseenter', onEnter)
77
+ target.addEventListener('mouseleave', onLeave)
78
+ target.addEventListener('focus', onEnter)
79
+ target.addEventListener('blur', onLeave)
80
+
81
+ this.cleanupFns.push(() => target.removeEventListener('mouseenter', onEnter))
82
+ this.cleanupFns.push(() => target.removeEventListener('mouseleave', onLeave))
83
+ this.cleanupFns.push(() => target.removeEventListener('focus', onEnter))
84
+ this.cleanupFns.push(() => target.removeEventListener('blur', onLeave))
85
+
86
+ if (this.popoverEl) {
87
+ const onPopoverEnter = () => {
88
+ if (this.hideTimer) {
89
+ clearTimeout(this.hideTimer)
90
+ this.hideTimer = null
91
+ }
92
+ }
93
+ const onPopoverLeave = () => {
94
+ this.hideTimer = setTimeout(() => this.hide(), 150)
95
+ }
96
+ this.popoverEl.addEventListener('mouseenter', onPopoverEnter)
97
+ this.popoverEl.addEventListener('mouseleave', onPopoverLeave)
98
+ this.cleanupFns.push(() => this.popoverEl!.removeEventListener('mouseenter', onPopoverEnter))
99
+ this.cleanupFns.push(() => this.popoverEl!.removeEventListener('mouseleave', onPopoverLeave))
100
+ }
101
+ }
102
+
103
+ target.appendChild(this.el)
104
+ }
105
+
106
+ private buildPopover(): void {
107
+ this.popoverEl = document.createElement('div')
108
+ this.popoverEl.className = 'mk-popover'
109
+ this.popoverEl.style.position = 'absolute'
110
+ this.popoverEl.style.zIndex = 'var(--mk-z-popover)'
111
+ this.popoverEl.setAttribute('role', this.options.trigger === 'click' ? 'dialog' : 'tooltip')
112
+ if (this.options.trigger === 'click') {
113
+ this.popoverEl.setAttribute('tabindex', '-1')
114
+ }
115
+ if (this.options.width) {
116
+ this.popoverEl.style.width = `${this.options.width}px`
117
+ }
118
+
119
+ this.arrowEl = document.createElement('div')
120
+ this.arrowEl.className = 'mk-popover__arrow'
121
+ this.popoverEl.appendChild(this.arrowEl)
122
+
123
+ if (this.options.title) {
124
+ const title = document.createElement('div')
125
+ title.className = 'mk-popover__title'
126
+ title.textContent = this.options.title
127
+ this.popoverEl.appendChild(title)
128
+ }
129
+
130
+ this.contentEl = document.createElement('div')
131
+ this.contentEl.className = 'mk-popover__content'
132
+ if (typeof this.options.content === 'string') {
133
+ this.contentEl.textContent = this.options.content
134
+ } else if (this.options.content) {
135
+ this.contentEl.appendChild(this.options.content)
136
+ }
137
+ this.popoverEl.appendChild(this.contentEl)
138
+
139
+ document.body.appendChild(this.popoverEl)
140
+ }
141
+
142
+ private position(): void {
143
+ if (!this.popoverEl || !this.arrowEl) return
144
+
145
+ const target = this.el.parentElement as HTMLElement
146
+ const rect = target.getBoundingClientRect()
147
+ const popRect = this.popoverEl.getBoundingClientRect()
148
+ const scrollX = window.scrollX
149
+ const scrollY = window.scrollY
150
+ const offset = this.options.offset!
151
+
152
+ let top = 0
153
+ let left = 0
154
+ let arrowClass = ''
155
+
156
+ switch (this.options.placement) {
157
+ case 'top':
158
+ top = rect.top + scrollY - popRect.height - offset
159
+ left = rect.left + scrollX + rect.width / 2 - popRect.width / 2
160
+ arrowClass = 'is-bottom'
161
+ break
162
+ case 'bottom':
163
+ top = rect.bottom + scrollY + offset
164
+ left = rect.left + scrollX + rect.width / 2 - popRect.width / 2
165
+ arrowClass = 'is-top'
166
+ break
167
+ case 'left':
168
+ top = rect.top + scrollY + rect.height / 2 - popRect.height / 2
169
+ left = rect.left + scrollX - popRect.width - offset
170
+ arrowClass = 'is-right'
171
+ break
172
+ case 'right':
173
+ top = rect.top + scrollY + rect.height / 2 - popRect.height / 2
174
+ left = rect.right + scrollX + offset
175
+ arrowClass = 'is-left'
176
+ break
177
+ default:
178
+ top = rect.top + scrollY - popRect.height - offset
179
+ left = rect.left + scrollX + rect.width / 2 - popRect.width / 2
180
+ arrowClass = 'is-bottom'
181
+ }
182
+
183
+ const padding = 8
184
+ if (left < padding) left = padding
185
+ if (left + popRect.width > window.innerWidth - padding) {
186
+ left = window.innerWidth - popRect.width - padding
187
+ }
188
+ if (top < padding) top = padding
189
+
190
+ this.popoverEl.style.top = `${top}px`
191
+ this.popoverEl.style.left = `${left}px`
192
+ this.arrowEl.className = `mk-popover__arrow ${arrowClass}`
193
+ }
194
+
195
+ show(): void {
196
+ if (!this.popoverEl) return
197
+ this.isVisible = true
198
+
199
+ this.popoverEl.classList.remove('is-visible')
200
+ this.popoverEl.style.visibility = 'hidden'
201
+ this.popoverEl.style.display = 'block'
202
+
203
+ requestAnimationFrame(() => {
204
+ if (!this.isVisible) return
205
+ this.position()
206
+ this.popoverEl!.style.visibility = 'visible'
207
+ this.popoverEl!.classList.add('is-visible')
208
+
209
+ if (this.options.trigger === 'click') {
210
+ if (!this.focusTrap) {
211
+ this.focusTrap = new FocusTrap(this.popoverEl!)
212
+ }
213
+ this.focusTrap.activate(this.popoverEl!)
214
+ this.escapeCleanup = onKey(this.popoverEl!, [
215
+ { key: Keys.Escape, handler: () => this.hide() },
216
+ ])
217
+ }
218
+ })
219
+ }
220
+
221
+ hide(): void {
222
+ if (!this.popoverEl) return
223
+ this.isVisible = false
224
+ this.popoverEl.classList.remove('is-visible')
225
+ if (this.escapeCleanup) {
226
+ this.escapeCleanup()
227
+ this.escapeCleanup = null
228
+ }
229
+ if (this.focusTrap) {
230
+ this.focusTrap.deactivate()
231
+ }
232
+ this.hideTimer = setTimeout(() => {
233
+ if (!this.isVisible) {
234
+ this.popoverEl!.style.display = 'none'
235
+ }
236
+ }, 200)
237
+ }
238
+
239
+ toggle(): void {
240
+ if (this.isVisible) {
241
+ this.hide()
242
+ } else {
243
+ this.show()
244
+ }
245
+ }
246
+
247
+ setContent(content: string | HTMLElement): void {
248
+ if (!this.contentEl) return
249
+ this.contentEl.innerHTML = ''
250
+ if (typeof content === 'string') {
251
+ this.contentEl.textContent = content
252
+ } else {
253
+ this.contentEl.appendChild(content)
254
+ }
255
+ }
256
+
257
+ destroy(): void {
258
+ this.cleanupFns.forEach((fn) => fn())
259
+ this.cleanupFns = []
260
+ if (this.showTimer) clearTimeout(this.showTimer)
261
+ if (this.hideTimer) clearTimeout(this.hideTimer)
262
+ if (this.escapeCleanup) {
263
+ this.escapeCleanup()
264
+ this.escapeCleanup = null
265
+ }
266
+ if (this.focusTrap) {
267
+ this.focusTrap.deactivate()
268
+ }
269
+ this.popoverEl?.remove()
270
+ this.el.remove()
271
+ }
272
+ }
273
+
274
+ export function createPopover(
275
+ target: HTMLElement,
276
+ options?: PopoverOptions
277
+ ): MkPopover {
278
+ return new MkPopover(target, options)
279
+ }
@@ -0,0 +1,112 @@
1
+ .mk-progress {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: var(--mk-space-3);
5
+ font-size: var(--mk-text-sm);
6
+ color: var(--mk-text-secondary);
7
+ }
8
+
9
+ .mk-progress.is-hide-info {
10
+ gap: 0;
11
+ }
12
+
13
+ /* Line */
14
+ .mk-progress--line {
15
+ width: 100%;
16
+ }
17
+
18
+ .mk-progress__track {
19
+ flex: 1;
20
+ height: 8px;
21
+ background: var(--mk-surface-hover);
22
+ border-radius: var(--mk-radius-full);
23
+ overflow: hidden;
24
+ }
25
+
26
+ .mk-progress__bar {
27
+ height: 100%;
28
+ background: var(--mk-primary);
29
+ border-radius: var(--mk-radius-full);
30
+ transition: width var(--mk-duration-slow) var(--mk-ease-out);
31
+ }
32
+
33
+ .mk-progress__text {
34
+ min-width: 36px;
35
+ text-align: right;
36
+ font-size: var(--mk-text-sm);
37
+ font-weight: var(--mk-font-medium);
38
+ color: var(--mk-text-secondary);
39
+ flex-shrink: 0;
40
+ }
41
+
42
+ .mk-progress.is-success .mk-progress__bar,
43
+ .mk-progress.is-success .mk-progress__path-circle {
44
+ background: var(--mk-success);
45
+ stroke: var(--mk-success);
46
+ }
47
+
48
+ .mk-progress.is-exception .mk-progress__bar,
49
+ .mk-progress.is-exception .mk-progress__path-circle {
50
+ background: var(--mk-danger);
51
+ stroke: var(--mk-danger);
52
+ }
53
+
54
+ .mk-progress.is-active .mk-progress__bar {
55
+ background: var(--mk-primary);
56
+ position: relative;
57
+ overflow: hidden;
58
+ }
59
+
60
+ .mk-progress.is-active .mk-progress__bar::after {
61
+ content: '';
62
+ position: absolute;
63
+ inset: 0;
64
+ background: linear-gradient(
65
+ 90deg,
66
+ transparent 0%,
67
+ rgba(255, 255, 255, 0.25) 50%,
68
+ transparent 100%
69
+ );
70
+ animation: mk-progress-active 1.5s ease-in-out infinite;
71
+ }
72
+
73
+ @keyframes mk-progress-active {
74
+ 0% { transform: translateX(-100%); }
75
+ 100% { transform: translateX(100%); }
76
+ }
77
+
78
+ /* Circle / Dashboard */
79
+ .mk-progress--circle,
80
+ .mk-progress--dashboard {
81
+ position: relative;
82
+ display: inline-flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ width: 120px;
86
+ height: 120px;
87
+ }
88
+
89
+ .mk-progress__svg {
90
+ position: absolute;
91
+ inset: 0;
92
+ }
93
+
94
+ .mk-progress__track-circle {
95
+ stroke: var(--mk-surface-hover);
96
+ }
97
+
98
+ .mk-progress__path-circle {
99
+ stroke: var(--mk-primary);
100
+ transition: stroke-dashoffset var(--mk-duration-slow) var(--mk-ease-out);
101
+ }
102
+
103
+ .mk-progress--circle .mk-progress__text,
104
+ .mk-progress--dashboard .mk-progress__text {
105
+ position: relative;
106
+ z-index: 1;
107
+ text-align: center;
108
+ min-width: auto;
109
+ font-size: var(--mk-text-md);
110
+ font-weight: var(--mk-font-semibold);
111
+ color: var(--mk-text);
112
+ }
@@ -0,0 +1,171 @@
1
+ import '../../styles/element-plus.css'
2
+ import './progress.css'
3
+
4
+ export interface ProgressOptions {
5
+ type?: 'line' | 'circle' | 'dashboard'
6
+ percent?: number
7
+ strokeWidth?: number
8
+ color?: string
9
+ status?: 'success' | 'exception' | 'active'
10
+ showInfo?: boolean
11
+ }
12
+
13
+ export class MkProgress {
14
+ el: HTMLDivElement
15
+ private options: ProgressOptions
16
+ private barEl: HTMLDivElement | null = null
17
+ private textEl: HTMLSpanElement | null = null
18
+ private circleSvg: SVGSVGElement | null = null
19
+ private circleTrack: SVGCircleElement | null = null
20
+ private circlePath: SVGCircleElement | null = null
21
+
22
+ constructor(container: HTMLElement | string, options: ProgressOptions = {}) {
23
+ const parent =
24
+ typeof container === 'string'
25
+ ? document.querySelector(container)!
26
+ : container
27
+
28
+ this.options = {
29
+ type: 'line',
30
+ percent: 0,
31
+ showInfo: true,
32
+ ...options,
33
+ }
34
+
35
+ this.el = document.createElement('div')
36
+ this.el.className = this.buildClass()
37
+
38
+ if (this.options.type === 'line') {
39
+ this.renderLine()
40
+ } else {
41
+ this.renderCircleOrDashboard()
42
+ }
43
+
44
+ parent.appendChild(this.el)
45
+
46
+ requestAnimationFrame(() => {
47
+ this.setPercent(this.options.percent!)
48
+ })
49
+ }
50
+
51
+ private buildClass(): string {
52
+ const classes = ['mk-progress', `mk-progress--${this.options.type}`]
53
+ if (this.options.status) classes.push(`is-${this.options.status}`)
54
+ if (!this.options.showInfo) classes.push('is-hide-info')
55
+ return classes.join(' ')
56
+ }
57
+
58
+ private renderLine(): void {
59
+ const track = document.createElement('div')
60
+ track.className = 'mk-progress__track'
61
+ if (this.options.strokeWidth) {
62
+ track.style.height = `${this.options.strokeWidth}px`
63
+ }
64
+
65
+ this.barEl = document.createElement('div')
66
+ this.barEl.className = 'mk-progress__bar'
67
+ track.appendChild(this.barEl)
68
+ this.el.appendChild(track)
69
+
70
+ if (this.options.showInfo) {
71
+ this.textEl = document.createElement('span')
72
+ this.textEl.className = 'mk-progress__text'
73
+ this.el.appendChild(this.textEl)
74
+ }
75
+ }
76
+
77
+ private renderCircleOrDashboard(): void {
78
+ const size = 120
79
+ const strokeWidth = this.options.strokeWidth || 6
80
+ const radius = (size - strokeWidth) / 2
81
+ const circumference = 2 * Math.PI * radius
82
+ const isDashboard = this.options.type === 'dashboard'
83
+ const dashArray = isDashboard ? circumference * 0.75 : circumference
84
+
85
+ this.circleSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
86
+ this.circleSvg.setAttribute('width', `${size}`)
87
+ this.circleSvg.setAttribute('height', `${size}`)
88
+ this.circleSvg.setAttribute('viewBox', `0 0 ${size} ${size}`)
89
+ this.circleSvg.classList.add('mk-progress__svg')
90
+
91
+ this.circleTrack = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
92
+ this.circleTrack.setAttribute('cx', `${size / 2}`)
93
+ this.circleTrack.setAttribute('cy', `${size / 2}`)
94
+ this.circleTrack.setAttribute('r', `${radius}`)
95
+ this.circleTrack.setAttribute('fill', 'none')
96
+ this.circleTrack.setAttribute('stroke-width', `${strokeWidth}`)
97
+ this.circleTrack.classList.add('mk-progress__track-circle')
98
+
99
+ this.circlePath = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
100
+ this.circlePath.setAttribute('cx', `${size / 2}`)
101
+ this.circlePath.setAttribute('cy', `${size / 2}`)
102
+ this.circlePath.setAttribute('r', `${radius}`)
103
+ this.circlePath.setAttribute('fill', 'none')
104
+ this.circlePath.setAttribute('stroke-width', `${strokeWidth}`)
105
+ this.circlePath.setAttribute('stroke-linecap', 'round')
106
+ this.circlePath.style.strokeDasharray = `${dashArray} ${circumference}`
107
+ this.circlePath.style.strokeDashoffset = `${dashArray}`
108
+ this.circlePath.style.transformOrigin = '50% 50%'
109
+
110
+ if (isDashboard) {
111
+ this.circleTrack.style.transform = 'rotate(135deg)'
112
+ this.circleTrack.style.transformOrigin = '50% 50%'
113
+ this.circlePath.style.transform = 'rotate(135deg)'
114
+ } else {
115
+ this.circleTrack.style.transform = 'rotate(-90deg)'
116
+ this.circleTrack.style.transformOrigin = '50% 50%'
117
+ this.circlePath.style.transform = 'rotate(-90deg)'
118
+ }
119
+
120
+ this.circlePath.classList.add('mk-progress__path-circle')
121
+
122
+ this.circleSvg.appendChild(this.circleTrack)
123
+ this.circleSvg.appendChild(this.circlePath)
124
+ this.el.appendChild(this.circleSvg)
125
+
126
+ if (this.options.showInfo) {
127
+ this.textEl = document.createElement('span')
128
+ this.textEl.className = 'mk-progress__text'
129
+ this.el.appendChild(this.textEl)
130
+ }
131
+ }
132
+
133
+ setPercent(percent: number): void {
134
+ const clamped = Math.max(0, Math.min(100, percent))
135
+ this.options.percent = clamped
136
+
137
+ if (this.options.type === 'line' && this.barEl) {
138
+ this.barEl.style.width = `${clamped}%`
139
+ if (this.options.color) {
140
+ this.barEl.style.background = this.options.color
141
+ }
142
+ } else if (this.circlePath && this.circleSvg) {
143
+ const size = 120
144
+ const strokeWidth = this.options.strokeWidth || 6
145
+ const radius = (size - strokeWidth) / 2
146
+ const circumference = 2 * Math.PI * radius
147
+ const isDashboard = this.options.type === 'dashboard'
148
+ const totalLength = isDashboard ? circumference * 0.75 : circumference
149
+ const offset = totalLength - (clamped / 100) * totalLength
150
+ this.circlePath.style.strokeDashoffset = `${offset}`
151
+ if (this.options.color) {
152
+ this.circlePath.style.stroke = this.options.color
153
+ }
154
+ }
155
+
156
+ if (this.textEl) {
157
+ this.textEl.textContent = `${Math.round(clamped)}%`
158
+ }
159
+ }
160
+
161
+ destroy(): void {
162
+ this.el.remove()
163
+ }
164
+ }
165
+
166
+ export function createProgress(
167
+ container: HTMLElement | string,
168
+ options?: ProgressOptions
169
+ ): MkProgress {
170
+ return new MkProgress(container, options)
171
+ }
@@ -0,0 +1,127 @@
1
+ .mk-steps {
2
+ display: flex;
3
+ width: 100%;
4
+ }
5
+
6
+ .mk-steps--horizontal {
7
+ flex-direction: row;
8
+ }
9
+
10
+ .mk-steps--vertical {
11
+ flex-direction: column;
12
+ }
13
+
14
+ .mk-step {
15
+ position: relative;
16
+ flex: 1;
17
+ display: flex;
18
+ flex-direction: column;
19
+ align-items: center;
20
+ text-align: center;
21
+ }
22
+
23
+ .mk-steps--vertical .mk-step {
24
+ flex-direction: row;
25
+ align-items: flex-start;
26
+ text-align: left;
27
+ padding-bottom: 16px;
28
+ }
29
+
30
+ .mk-step__head {
31
+ position: relative;
32
+ width: 32px;
33
+ height: 32px;
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ }
38
+
39
+ .mk-step__line {
40
+ position: absolute;
41
+ top: 50%;
42
+ left: 50%;
43
+ width: 100%;
44
+ height: 1px;
45
+ background: var(--mk-border);
46
+ transform: translateY(-50%);
47
+ z-index: 0;
48
+ }
49
+
50
+ .mk-step.is-last .mk-step__line {
51
+ display: none;
52
+ }
53
+
54
+ .mk-step.is-finish .mk-step__line {
55
+ background: var(--mk-primary);
56
+ }
57
+
58
+ .mk-step__icon {
59
+ position: relative;
60
+ z-index: 1;
61
+ width: 28px;
62
+ height: 28px;
63
+ border-radius: 50%;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ font-size: 12px;
68
+ font-weight: 600;
69
+ background: var(--mk-surface);
70
+ border: 2px solid var(--mk-border);
71
+ color: var(--mk-text-tertiary);
72
+ transition: var(--mk-transition-colors);
73
+ }
74
+
75
+ .mk-step.is-process .mk-step__icon {
76
+ background: var(--mk-primary);
77
+ border-color: var(--mk-primary);
78
+ color: #fff;
79
+ box-shadow: 0 0 0 4px var(--mk-primary-muted);
80
+ }
81
+
82
+ .mk-step.is-finish .mk-step__icon {
83
+ background: var(--mk-primary);
84
+ border-color: var(--mk-primary);
85
+ color: #fff;
86
+ }
87
+
88
+ .mk-step.is-error .mk-step__icon {
89
+ background: var(--mk-danger);
90
+ border-color: var(--mk-danger);
91
+ color: #fff;
92
+ }
93
+
94
+ .mk-step__main {
95
+ margin-top: 8px;
96
+ }
97
+
98
+ .mk-steps--vertical .mk-step__main {
99
+ margin-top: 0;
100
+ margin-left: 12px;
101
+ }
102
+
103
+ .mk-step__title {
104
+ font-size: var(--mk-text-sm);
105
+ font-weight: var(--mk-font-medium);
106
+ color: var(--mk-text);
107
+ }
108
+
109
+ .mk-step.is-wait .mk-step__title {
110
+ color: var(--mk-text-tertiary);
111
+ }
112
+
113
+ .mk-step__description {
114
+ font-size: var(--mk-text-xs);
115
+ color: var(--mk-text-tertiary);
116
+ margin-top: 4px;
117
+ }
118
+
119
+ .mk-steps--small .mk-step__icon {
120
+ width: 22px;
121
+ height: 22px;
122
+ font-size: 10px;
123
+ }
124
+
125
+ .mk-steps--small .mk-step__title {
126
+ font-size: var(--mk-text-xs);
127
+ }