@luanlu/mk-motion 1.2.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 (98) hide show
  1. package/package.json +2 -4
  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/presets/index.ts +69 -0
  82. package/src/scroll/scroll-trigger.ts +104 -0
  83. package/src/styles/animations.css +135 -0
  84. package/src/styles/element-plus.css +174 -0
  85. package/src/text/count-up.ts +108 -0
  86. package/src/text/typewriter.ts +109 -0
  87. package/src/theme/dark.css +19 -0
  88. package/src/theme/light.css +19 -0
  89. package/src/theme/theme.ts +65 -0
  90. package/src/transitions/blur-reveal.ts +92 -0
  91. package/src/transitions/collapse.ts +112 -0
  92. package/src/transitions/lazy-image.ts +87 -0
  93. package/src/transitions/list.ts +75 -0
  94. package/src/transitions/loading.ts +95 -0
  95. package/src/transitions/parallax.ts +60 -0
  96. package/src/transitions/shimmer.ts +105 -0
  97. package/src/transitions/toast.ts +151 -0
  98. package/src/types.d.ts +4 -0
@@ -0,0 +1,119 @@
1
+ import './checkbox.css'
2
+ import { onKey, Keys } from '../../a11y/keyboard.ts'
3
+
4
+ export interface CheckboxOptions {
5
+ label?: string
6
+ checked?: boolean
7
+ disabled?: boolean
8
+ onChange?: (checked: boolean) => void
9
+ }
10
+
11
+ export class MkCheckbox {
12
+ el: HTMLLabelElement
13
+ private options: CheckboxOptions
14
+ private _checked: boolean
15
+ private _cleanupKey?: () => void
16
+
17
+ constructor(container: HTMLElement | string, options: CheckboxOptions = {}) {
18
+ const parent =
19
+ typeof container === 'string'
20
+ ? document.querySelector(container)!
21
+ : container
22
+
23
+ this.options = { checked: false, ...options }
24
+ this._checked = this.options.checked!
25
+
26
+ this.el = document.createElement('label')
27
+ this.el.className = 'mk-checkbox'
28
+ if (this._checked) this.el.classList.add('is-checked')
29
+ if (this.options.disabled) this.el.classList.add('is-disabled')
30
+ this.el.setAttribute('role', 'checkbox')
31
+ this.el.setAttribute('aria-checked', String(this._checked))
32
+ if (!this.options.disabled) {
33
+ this.el.setAttribute('tabindex', '0')
34
+ }
35
+
36
+ const box = document.createElement('span')
37
+ box.className = 'mk-checkbox__input'
38
+ const check = document.createElement('span')
39
+ check.className = 'mk-checkbox__check'
40
+ check.textContent = '✓'
41
+ box.appendChild(check)
42
+ this.el.appendChild(box)
43
+
44
+ if (this.options.label) {
45
+ const text = document.createElement('span')
46
+ text.textContent = this.options.label
47
+ this.el.appendChild(text)
48
+ }
49
+
50
+ this.el.addEventListener('click', () => {
51
+ if (this.options.disabled) return
52
+ this.toggle()
53
+ })
54
+
55
+ this._cleanupKey = onKey(this.el, [
56
+ { key: Keys.Space, handler: () => this.toggle() },
57
+ ])
58
+
59
+ parent.appendChild(this.el)
60
+ }
61
+
62
+ get checked(): boolean {
63
+ return this._checked
64
+ }
65
+
66
+ set checked(v: boolean) {
67
+ this._checked = v
68
+ this.el.classList.toggle('is-checked', v)
69
+ this.el.setAttribute('aria-checked', String(v))
70
+ this.options.onChange?.(v)
71
+ }
72
+
73
+ toggle(): void {
74
+ this.checked = !this._checked
75
+ }
76
+
77
+ destroy(): void {
78
+ this._cleanupKey?.()
79
+ this.el.remove()
80
+ }
81
+ }
82
+
83
+ export function createCheckbox(
84
+ container: HTMLElement | string,
85
+ options?: CheckboxOptions
86
+ ): MkCheckbox {
87
+ return new MkCheckbox(container, options)
88
+ }
89
+
90
+ /* ===== Checkbox Group ===== */
91
+ export class MkCheckboxGroup {
92
+ el: HTMLDivElement
93
+ private checkboxes: MkCheckbox[] = []
94
+
95
+ constructor(container: HTMLElement | string) {
96
+ const parent =
97
+ typeof container === 'string'
98
+ ? document.querySelector(container)!
99
+ : container
100
+
101
+ this.el = document.createElement('div')
102
+ this.el.className = 'mk-checkbox-group'
103
+ parent.appendChild(this.el)
104
+ }
105
+
106
+ add(options: CheckboxOptions): MkCheckbox {
107
+ const cb = new MkCheckbox(this.el, options)
108
+ this.checkboxes.push(cb)
109
+ return cb
110
+ }
111
+
112
+ getValues(): boolean[] {
113
+ return this.checkboxes.map((cb) => cb.checked)
114
+ }
115
+
116
+ destroy(): void {
117
+ this.el.remove()
118
+ }
119
+ }
@@ -0,0 +1,57 @@
1
+ .mk-radio {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 8px;
5
+ cursor: pointer;
6
+ user-select: none;
7
+ font-size: 13px;
8
+ color: var(--mk-text);
9
+ }
10
+
11
+ .mk-radio__input {
12
+ width: 16px;
13
+ height: 16px;
14
+ border: 1.5px solid var(--mk-border);
15
+ border-radius: 50%;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ transition: var(--mk-transition);
20
+ flex-shrink: 0;
21
+ background: var(--mk-surface);
22
+ }
23
+
24
+ .mk-radio:hover .mk-radio__input {
25
+ border-color: var(--mk-border-hover);
26
+ }
27
+
28
+ .mk-radio.is-checked .mk-radio__input {
29
+ border-color: var(--mk-primary);
30
+ }
31
+
32
+ .mk-radio__dot {
33
+ width: 8px;
34
+ height: 8px;
35
+ border-radius: 50%;
36
+ background: var(--mk-primary);
37
+ opacity: 0;
38
+ transform: scale(0);
39
+ transition: all 0.15s ease;
40
+ }
41
+
42
+ .mk-radio.is-checked .mk-radio__dot {
43
+ opacity: 1;
44
+ transform: scale(1);
45
+ }
46
+
47
+ .mk-radio.is-disabled {
48
+ opacity: 0.4;
49
+ cursor: not-allowed;
50
+ }
51
+
52
+ /* Group */
53
+ .mk-radio-group {
54
+ display: flex;
55
+ flex-wrap: wrap;
56
+ gap: 16px;
57
+ }
@@ -0,0 +1,153 @@
1
+ import './radio.css'
2
+ import { onKey, Keys } from '../../a11y/keyboard.ts'
3
+
4
+ export interface RadioOptions {
5
+ label?: string
6
+ value: string | number
7
+ checked?: boolean
8
+ disabled?: boolean
9
+ onChange?: (value: string | number) => void
10
+ }
11
+
12
+ export class MkRadio {
13
+ el: HTMLLabelElement
14
+ private options: RadioOptions
15
+ private _checked: boolean
16
+
17
+ constructor(container: HTMLElement | string, options: RadioOptions) {
18
+ const parent =
19
+ typeof container === 'string'
20
+ ? document.querySelector(container)!
21
+ : container
22
+
23
+ this.options = { checked: false, ...options }
24
+ this._checked = this.options.checked!
25
+
26
+ this.el = document.createElement('label')
27
+ this.el.className = 'mk-radio'
28
+ if (this._checked) this.el.classList.add('is-checked')
29
+ if (this.options.disabled) this.el.classList.add('is-disabled')
30
+ this.el.setAttribute('role', 'radio')
31
+ this.el.setAttribute('aria-checked', String(this._checked))
32
+
33
+ const box = document.createElement('span')
34
+ box.className = 'mk-radio__input'
35
+ const dot = document.createElement('span')
36
+ dot.className = 'mk-radio__dot'
37
+ box.appendChild(dot)
38
+ this.el.appendChild(box)
39
+
40
+ if (this.options.label) {
41
+ const text = document.createElement('span')
42
+ text.textContent = this.options.label
43
+ this.el.appendChild(text)
44
+ }
45
+
46
+ this.el.addEventListener('click', () => {
47
+ if (this.options.disabled) return
48
+ this.options.onChange?.(this.options.value)
49
+ })
50
+
51
+ parent.appendChild(this.el)
52
+ }
53
+
54
+ setChecked(checked: boolean): void {
55
+ this._checked = checked
56
+ this.el.classList.toggle('is-checked', checked)
57
+ this.el.setAttribute('aria-checked', String(checked))
58
+ }
59
+
60
+ getValue(): string | number {
61
+ return this.options.value
62
+ }
63
+
64
+ destroy(): void {
65
+ this.el.remove()
66
+ }
67
+ }
68
+
69
+ /* ===== Radio Group ===== */
70
+ export class MkRadioGroup {
71
+ el: HTMLDivElement
72
+ private radios: MkRadio[] = []
73
+ private _value: string | number | undefined
74
+ private _cleanupKey?: () => void
75
+
76
+ constructor(
77
+ container: HTMLElement | string,
78
+ options: { value?: string | number; onChange?: (value: string | number) => void } = {}
79
+ ) {
80
+ const parent =
81
+ typeof container === 'string'
82
+ ? document.querySelector(container)!
83
+ : container
84
+
85
+ this._value = options.value
86
+
87
+ this.el = document.createElement('div')
88
+ this.el.className = 'mk-radio-group'
89
+ this.el.setAttribute('role', 'radiogroup')
90
+ parent.appendChild(this.el)
91
+
92
+ this._cleanupKey = onKey(this.el, [
93
+ { key: Keys.ArrowUp, handler: () => this.focusRadio(-1) },
94
+ { key: Keys.ArrowLeft, handler: () => this.focusRadio(-1) },
95
+ { key: Keys.ArrowDown, handler: () => this.focusRadio(1) },
96
+ { key: Keys.ArrowRight, handler: () => this.focusRadio(1) },
97
+ ])
98
+ }
99
+
100
+ add(radioOptions: Omit<RadioOptions, 'onChange'>): MkRadio {
101
+ const radio = new MkRadio(this.el, {
102
+ ...radioOptions,
103
+ onChange: (value) => {
104
+ this.setValue(value)
105
+ },
106
+ })
107
+ const checked = radioOptions.value === this._value
108
+ radio.setChecked(checked)
109
+ if (this._value === undefined && this.radios.length === 0) {
110
+ radio.el.setAttribute('tabindex', '0')
111
+ } else {
112
+ radio.el.setAttribute('tabindex', checked ? '0' : '-1')
113
+ }
114
+ this.radios.push(radio)
115
+ return radio
116
+ }
117
+
118
+ setValue(value: string | number): void {
119
+ this._value = value
120
+ this.radios.forEach((r) => {
121
+ const checked = r.getValue() === value
122
+ r.setChecked(checked)
123
+ r.el.setAttribute('tabindex', checked ? '0' : '-1')
124
+ })
125
+ }
126
+
127
+ getValue(): string | number | undefined {
128
+ return this._value
129
+ }
130
+
131
+ private focusRadio(dir: number): void {
132
+ let current = this.radios.findIndex((r) => r.el.getAttribute('tabindex') === '0')
133
+ if (current === -1) current = this.radios.findIndex((r) => r.el.classList.contains('is-checked'))
134
+ if (current === -1) current = 0
135
+ let next = current
136
+ for (let i = 0; i < this.radios.length; i++) {
137
+ next = (next + dir + this.radios.length) % this.radios.length
138
+ if (!this.radios[next].el.classList.contains('is-disabled')) {
139
+ break
140
+ }
141
+ }
142
+ if (next !== current || this.radios.length === 1) {
143
+ this.radios.forEach((r, i) => r.el.setAttribute('tabindex', i === next ? '0' : '-1'))
144
+ this.radios[next].el.focus()
145
+ this.setValue(this.radios[next].getValue())
146
+ }
147
+ }
148
+
149
+ destroy(): void {
150
+ this._cleanupKey?.()
151
+ this.el.remove()
152
+ }
153
+ }
@@ -0,0 +1,91 @@
1
+ .mk-select {
2
+ position: relative;
3
+ display: inline-flex;
4
+ width: 100%;
5
+ max-width: 320px;
6
+ }
7
+
8
+ .mk-select__trigger {
9
+ width: 100%;
10
+ height: 36px;
11
+ padding: 0 32px 0 12px;
12
+ font-size: 13px;
13
+ color: var(--mk-text);
14
+ background: var(--mk-surface);
15
+ border: 1px solid var(--mk-border);
16
+ border-radius: var(--mk-radius);
17
+ cursor: pointer;
18
+ display: flex;
19
+ align-items: center;
20
+ outline: none;
21
+ transition: var(--mk-transition);
22
+ user-select: none;
23
+ }
24
+
25
+ .mk-select__trigger:hover {
26
+ border-color: var(--mk-border-hover);
27
+ }
28
+
29
+ .mk-select__trigger.is-open {
30
+ border-color: var(--mk-primary);
31
+ }
32
+
33
+ .mk-select__arrow {
34
+ position: absolute;
35
+ right: 10px;
36
+ color: var(--mk-text-tertiary);
37
+ font-size: 10px;
38
+ transition: transform 0.2s;
39
+ }
40
+
41
+ .mk-select__trigger.is-open .mk-select__arrow {
42
+ transform: rotate(180deg);
43
+ }
44
+
45
+ .mk-select__dropdown {
46
+ position: absolute;
47
+ top: calc(100% + 4px);
48
+ left: 0;
49
+ right: 0;
50
+ background: var(--mk-surface);
51
+ border: 1px solid var(--mk-border);
52
+ border-radius: var(--mk-radius);
53
+ z-index: 100;
54
+ max-height: 240px;
55
+ overflow-y: auto;
56
+ opacity: 0;
57
+ transform: translateY(-4px);
58
+ pointer-events: none;
59
+ transition: all 0.15s ease;
60
+ box-shadow: var(--mk-shadow-lg);
61
+ }
62
+
63
+ .mk-select__dropdown.is-open {
64
+ opacity: 1;
65
+ transform: translateY(0);
66
+ pointer-events: auto;
67
+ }
68
+
69
+ .mk-select__option {
70
+ padding: 8px 12px;
71
+ font-size: 13px;
72
+ color: var(--mk-text);
73
+ cursor: pointer;
74
+ transition: var(--mk-transition);
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 8px;
78
+ }
79
+
80
+ .mk-select__option:hover {
81
+ background: var(--mk-surface-hover);
82
+ }
83
+
84
+ .mk-select__option.is-selected {
85
+ background: rgba(99,102,241,0.1);
86
+ color: var(--mk-primary);
87
+ }
88
+
89
+ .mk-select__placeholder {
90
+ color: var(--mk-text-tertiary);
91
+ }
@@ -0,0 +1,174 @@
1
+ import './select.css'
2
+ import { onKey, Keys } from '../../a11y/keyboard.ts'
3
+
4
+ export interface SelectOption {
5
+ label: string
6
+ value: string | number
7
+ disabled?: boolean
8
+ }
9
+
10
+ export interface SelectOptions {
11
+ placeholder?: string
12
+ options: SelectOption[]
13
+ value?: string | number
14
+ onChange?: (value: string | number) => void
15
+ }
16
+
17
+ export class MkSelect {
18
+ el: HTMLDivElement
19
+ private trigger: HTMLDivElement
20
+ private dropdown: HTMLDivElement
21
+ private options: SelectOptions
22
+ private _value: string | number | undefined
23
+ private isOpen = false
24
+ private _cleanupKey?: () => void
25
+
26
+ constructor(container: HTMLElement | string, options: SelectOptions) {
27
+ const parent =
28
+ typeof container === 'string'
29
+ ? document.querySelector(container)!
30
+ : container
31
+
32
+ this.options = options
33
+ this._value = options.value
34
+
35
+ this.el = document.createElement('div')
36
+ this.el.className = 'mk-select'
37
+
38
+ this.trigger = document.createElement('div')
39
+ this.trigger.className = 'mk-select__trigger'
40
+ this.trigger.setAttribute('role', 'combobox')
41
+ this.trigger.setAttribute('aria-haspopup', 'listbox')
42
+ this.trigger.setAttribute('aria-expanded', 'false')
43
+ this.trigger.setAttribute('tabindex', '0')
44
+ this.trigger.addEventListener('click', () => this.toggle())
45
+
46
+ const label = document.createElement('span')
47
+ label.className = 'mk-select__label'
48
+ this.updateLabel(label)
49
+ this.trigger.appendChild(label)
50
+
51
+ const arrow = document.createElement('span')
52
+ arrow.className = 'mk-select__arrow'
53
+ arrow.textContent = '▼'
54
+ this.trigger.appendChild(arrow)
55
+
56
+ this.dropdown = document.createElement('div')
57
+ this.dropdown.className = 'mk-select__dropdown'
58
+ this.dropdown.setAttribute('role', 'listbox')
59
+
60
+ options.options.forEach((opt) => {
61
+ const item = document.createElement('div')
62
+ item.className = 'mk-select__option'
63
+ item.textContent = opt.label
64
+ item.setAttribute('role', 'option')
65
+ item.setAttribute('aria-selected', String(opt.value === this._value))
66
+ if (opt.disabled) {
67
+ item.setAttribute('aria-disabled', 'true')
68
+ item.style.opacity = '0.4'
69
+ item.style.pointerEvents = 'none'
70
+ }
71
+ item.addEventListener('click', () => {
72
+ this.setValue(opt.value)
73
+ this.close()
74
+ })
75
+ this.dropdown.appendChild(item)
76
+ })
77
+
78
+ this.el.appendChild(this.trigger)
79
+ this.el.appendChild(this.dropdown)
80
+ parent.appendChild(this.el)
81
+
82
+ this._cleanupKey = onKey(this.trigger, [
83
+ { key: Keys.ArrowDown, handler: () => this.moveSelection(1) },
84
+ { key: Keys.ArrowUp, handler: () => this.moveSelection(-1) },
85
+ { key: Keys.Enter, handler: () => {
86
+ if (this.isOpen) this.close()
87
+ else this.open()
88
+ }},
89
+ { key: Keys.Escape, handler: () => this.close() },
90
+ ])
91
+
92
+ document.addEventListener('click', (e) => {
93
+ if (!this.el.contains(e.target as Node)) this.close()
94
+ })
95
+ }
96
+
97
+ private updateLabel(labelEl: HTMLSpanElement): void {
98
+ const selected = this.options.options.find((o) => o.value === this._value)
99
+ if (selected) {
100
+ labelEl.textContent = selected.label
101
+ labelEl.classList.remove('mk-select__placeholder')
102
+ } else {
103
+ labelEl.textContent = this.options.placeholder || '请选择'
104
+ labelEl.classList.add('mk-select__placeholder')
105
+ }
106
+ }
107
+
108
+ get value(): string | number | undefined {
109
+ return this._value
110
+ }
111
+
112
+ setValue(v: string | number): void {
113
+ this._value = v
114
+ const label = this.trigger.querySelector('.mk-select__label') as HTMLSpanElement
115
+ this.updateLabel(label)
116
+ this.renderOptions()
117
+ this.options.onChange?.(v)
118
+ }
119
+
120
+ private renderOptions(): void {
121
+ const items = this.dropdown.querySelectorAll('.mk-select__option')
122
+ items.forEach((item, i) => {
123
+ const opt = this.options.options[i]
124
+ item.classList.toggle('is-selected', opt.value === this._value)
125
+ item.setAttribute('aria-selected', String(opt.value === this._value))
126
+ })
127
+ }
128
+
129
+ private toggle(): void {
130
+ this.isOpen ? this.close() : this.open()
131
+ }
132
+
133
+ open(): void {
134
+ this.isOpen = true
135
+ this.trigger.setAttribute('aria-expanded', 'true')
136
+ this.trigger.classList.add('is-open')
137
+ this.dropdown.classList.add('is-open')
138
+ this.renderOptions()
139
+ }
140
+
141
+ close(): void {
142
+ this.isOpen = false
143
+ this.trigger.setAttribute('aria-expanded', 'false')
144
+ this.trigger.classList.remove('is-open')
145
+ this.dropdown.classList.remove('is-open')
146
+ }
147
+
148
+ private moveSelection(dir: number): void {
149
+ if (!this.isOpen) {
150
+ this.open()
151
+ }
152
+ const currentIndex = this.options.options.findIndex((o) => o.value === this._value)
153
+ let next = currentIndex
154
+ for (let i = 0; i < this.options.options.length; i++) {
155
+ next = (next + dir + this.options.options.length) % this.options.options.length
156
+ if (!this.options.options[next].disabled) {
157
+ this.setValue(this.options.options[next].value)
158
+ return
159
+ }
160
+ }
161
+ }
162
+
163
+ destroy(): void {
164
+ this._cleanupKey?.()
165
+ this.el.remove()
166
+ }
167
+ }
168
+
169
+ export function createSelect(
170
+ container: HTMLElement | string,
171
+ options: SelectOptions
172
+ ): MkSelect {
173
+ return new MkSelect(container, options)
174
+ }
@@ -0,0 +1,56 @@
1
+ .mk-slider {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 12px;
5
+ width: 100%;
6
+ max-width: 320px;
7
+ }
8
+
9
+ .mk-slider__track {
10
+ position: relative;
11
+ flex: 1;
12
+ height: 4px;
13
+ background: var(--mk-border);
14
+ border-radius: 2px;
15
+ cursor: pointer;
16
+ }
17
+
18
+ .mk-slider__fill {
19
+ position: absolute;
20
+ left: 0;
21
+ top: 0;
22
+ height: 100%;
23
+ background: var(--mk-primary);
24
+ border-radius: 2px;
25
+ pointer-events: none;
26
+ }
27
+
28
+ .mk-slider__thumb {
29
+ position: absolute;
30
+ top: 50%;
31
+ width: 16px;
32
+ height: 16px;
33
+ background: var(--mk-primary);
34
+ border: 2px solid var(--mk-surface);
35
+ border-radius: 50%;
36
+ transform: translate(-50%, -50%);
37
+ cursor: grab;
38
+ transition: transform 0.15s, box-shadow 0.15s;
39
+ box-shadow: var(--mk-shadow-sm);
40
+ }
41
+
42
+ .mk-slider__thumb:hover {
43
+ transform: translate(-50%, -50%) scale(1.2);
44
+ }
45
+
46
+ .mk-slider__thumb:active {
47
+ cursor: grabbing;
48
+ }
49
+
50
+ .mk-slider__value {
51
+ font-size: 13px;
52
+ color: var(--mk-text-secondary);
53
+ min-width: 32px;
54
+ text-align: right;
55
+ font-variant-numeric: tabular-nums;
56
+ }