@asd20/ui-next 2.0.1 → 2.0.3
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.3](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.2...ui-next-v2.0.3) (2026-03-27)
|
|
4
|
+
|
|
5
|
+
## [2.0.2](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.1...ui-next-v2.0.2) (2026-03-27)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* replace basicscroll scroll-track runtime ([c0fe361](https://github.com/academydistrict20/asd20-ui-next/commit/c0fe361c301bbf1d132eac7876c5e02cabe4a5b0))
|
|
11
|
+
|
|
3
12
|
## [2.0.1](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.0...ui-next-v2.0.1) (2026-03-27)
|
|
4
13
|
|
|
5
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asd20/ui-next",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ASD20 UI component library for Vue 3.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"axios": "^1.13.6",
|
|
37
|
-
"basicscroll": "^3.0.2",
|
|
38
37
|
"countup.js": "^2.0.0",
|
|
39
38
|
"date-fns": "^2.30.0",
|
|
40
39
|
"date-fns-tz": "^1.1.7",
|
|
@@ -1,32 +1,190 @@
|
|
|
1
|
-
|
|
1
|
+
const DEFAULT_VARIABLE_NAME = 'scroll-progress'
|
|
2
2
|
|
|
3
|
-
function
|
|
3
|
+
function clamp(value, min, max) {
|
|
4
|
+
return Math.min(Math.max(value, min), max)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function getScrollTop() {
|
|
8
|
+
const scrollingElement =
|
|
9
|
+
document.scrollingElement || document.documentElement || document.body
|
|
10
|
+
|
|
11
|
+
return scrollingElement ? scrollingElement.scrollTop : 0
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getViewportHeight() {
|
|
15
|
+
return (
|
|
16
|
+
window.innerHeight ||
|
|
17
|
+
window.outerHeight ||
|
|
18
|
+
document.documentElement?.clientHeight ||
|
|
19
|
+
1
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseAbsoluteValue(value) {
|
|
24
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof value !== 'string') {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parsedValue = Number.parseFloat(value)
|
|
33
|
+
|
|
34
|
+
return Number.isFinite(parsedValue) ? parsedValue : null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveKeywordValue(value, el, scrollTop, viewportHeight) {
|
|
38
|
+
if (!el || typeof value !== 'string') {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const match = value.match(/^(top|middle|bottom)-(top|middle|bottom)$/)
|
|
43
|
+
|
|
44
|
+
if (!match) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const [, elementEdge, viewportEdge] = match
|
|
49
|
+
const rect = el.getBoundingClientRect()
|
|
50
|
+
let absoluteOffset = scrollTop + rect.top
|
|
51
|
+
|
|
52
|
+
if (elementEdge === 'middle') {
|
|
53
|
+
absoluteOffset += rect.height / 2
|
|
54
|
+
} else if (elementEdge === 'bottom') {
|
|
55
|
+
absoluteOffset += rect.height
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (viewportEdge === 'middle') {
|
|
59
|
+
absoluteOffset -= viewportHeight / 2
|
|
60
|
+
} else if (viewportEdge === 'bottom') {
|
|
61
|
+
absoluteOffset -= viewportHeight
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return absoluteOffset
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveBoundary(value, el, scrollTop, viewportHeight) {
|
|
68
|
+
const absoluteValue = parseAbsoluteValue(value)
|
|
69
|
+
|
|
70
|
+
if (absoluteValue !== null) {
|
|
71
|
+
return absoluteValue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return resolveKeywordValue(value, el, scrollTop, viewportHeight)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getBindingConfig(el, { modifiers = {}, expression } = {}) {
|
|
78
|
+
const viewportHeight = getViewportHeight()
|
|
79
|
+
const scrollTop = getScrollTop()
|
|
80
|
+
const from = modifiers.window
|
|
81
|
+
? 0
|
|
82
|
+
: resolveBoundary('middle-top', el, scrollTop, viewportHeight)
|
|
83
|
+
const to = modifiers.window
|
|
84
|
+
? window.outerHeight || viewportHeight
|
|
85
|
+
: resolveBoundary('middle-bottom', el, scrollTop, viewportHeight)
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
cssVariable: `--${expression || DEFAULT_VARIABLE_NAME}`,
|
|
89
|
+
from,
|
|
90
|
+
to,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function setProgress(el, cssVariable, progress) {
|
|
95
|
+
const normalizedProgress = Number.isFinite(progress)
|
|
96
|
+
? Math.round(progress * 10000) / 10000
|
|
97
|
+
: 0
|
|
98
|
+
|
|
99
|
+
el.style.setProperty(cssVariable, String(normalizedProgress))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function updateScrollTrack(el) {
|
|
103
|
+
if (typeof window === 'undefined' || !el.__scrollTrackState) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const scrollTop = getScrollTop()
|
|
108
|
+
const { cssVariable, from, to } = getBindingConfig(
|
|
109
|
+
el,
|
|
110
|
+
el.__scrollTrackState.binding
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if (from === null || to === null) {
|
|
114
|
+
setProgress(el, cssVariable, 0)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (from === to) {
|
|
119
|
+
setProgress(el, cssVariable, scrollTop >= to ? 1 : 0)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const progress = clamp((scrollTop - from) / (to - from), 0, 1)
|
|
124
|
+
|
|
125
|
+
setProgress(el, cssVariable, progress)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function mountScrollTrack(el, binding = {}) {
|
|
4
129
|
if (typeof window === 'undefined') return
|
|
5
130
|
|
|
6
|
-
el
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
131
|
+
unmountScrollTrack(el)
|
|
132
|
+
|
|
133
|
+
const state = {
|
|
134
|
+
binding,
|
|
135
|
+
frameId: null,
|
|
136
|
+
handleUpdate() {
|
|
137
|
+
updateScrollTrack(el)
|
|
138
|
+
},
|
|
139
|
+
scheduleUpdate() {
|
|
140
|
+
if (state.frameId !== null) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
state.frameId = window.requestAnimationFrame(() => {
|
|
145
|
+
state.frameId = null
|
|
146
|
+
state.handleUpdate()
|
|
147
|
+
})
|
|
16
148
|
},
|
|
17
|
-
}
|
|
18
|
-
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
el.__scrollTrackState = state
|
|
152
|
+
updateScrollTrack(el)
|
|
153
|
+
|
|
154
|
+
window.addEventListener('scroll', state.scheduleUpdate, { passive: true })
|
|
155
|
+
window.addEventListener('resize', state.scheduleUpdate)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function updateBinding(el, binding = {}) {
|
|
159
|
+
if (!el.__scrollTrackState) {
|
|
160
|
+
mountScrollTrack(el, binding)
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
el.__scrollTrackState.binding = binding
|
|
165
|
+
updateScrollTrack(el)
|
|
19
166
|
}
|
|
20
167
|
|
|
21
168
|
function unmountScrollTrack(el) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
169
|
+
const state = el.__scrollTrackState
|
|
170
|
+
|
|
171
|
+
if (!state || typeof window === 'undefined') return
|
|
172
|
+
|
|
173
|
+
window.removeEventListener('scroll', state.scheduleUpdate)
|
|
174
|
+
window.removeEventListener('resize', state.scheduleUpdate)
|
|
175
|
+
|
|
176
|
+
if (state.frameId !== null) {
|
|
177
|
+
window.cancelAnimationFrame(state.frameId)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
delete el.__scrollTrackState
|
|
25
181
|
}
|
|
26
182
|
|
|
27
183
|
export default {
|
|
28
184
|
inserted: mountScrollTrack,
|
|
29
185
|
mounted: mountScrollTrack,
|
|
186
|
+
update: updateBinding,
|
|
187
|
+
updated: updateBinding,
|
|
30
188
|
unbind: unmountScrollTrack,
|
|
31
189
|
unmounted: unmountScrollTrack,
|
|
32
190
|
}
|
|
@@ -50,20 +50,14 @@ export function shouldOpenInNewWindow(href, options) {
|
|
|
50
50
|
return !isAsd20Hostname(url.hostname, options)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export function resolveCurrentHostname(
|
|
53
|
+
export function resolveCurrentHostname(_vm, options = {}) {
|
|
54
54
|
if (typeof window !== 'undefined' && window.location) {
|
|
55
55
|
return normalizeHostname(window.location.hostname, options)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
vm.$ssrContext.req &&
|
|
62
|
-
vm.$ssrContext.req.headers &&
|
|
63
|
-
(vm.$ssrContext.req.headers['x-forwarded-host'] ||
|
|
64
|
-
vm.$ssrContext.req.headers.host)
|
|
65
|
-
|
|
66
|
-
return normalizeHostname(requestHost, options)
|
|
58
|
+
// Server renders should stay deterministic and avoid reaching through the
|
|
59
|
+
// component proxy for SSR internals that are not part of the Vue 3 public API.
|
|
60
|
+
return normalizeHostname(options.currentHostname || '', options)
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
export function shouldShowExternalIcon(href, currentHostname, options) {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import kebabCase from 'lodash/kebabCase'
|
|
2
|
+
import createComponentInstanceId from '../utils/createComponentInstanceId'
|
|
2
3
|
|
|
3
4
|
const INPUT_MODEL_LISTENER_KEYS = ['onInput', 'onUpdate:modelValue']
|
|
5
|
+
const DEFAULT_INPUT_COMPONENT_NAME = 'asd20-input'
|
|
6
|
+
|
|
7
|
+
function resolveKebabComponentName(componentName = DEFAULT_INPUT_COMPONENT_NAME) {
|
|
8
|
+
return kebabCase(componentName).replace(/-([0-9])/g, '$1')
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
function omitKeys(source, keys) {
|
|
6
12
|
const target = { ...(source || {}) }
|
|
@@ -46,18 +52,23 @@ export default {
|
|
|
46
52
|
reversed: { type: Boolean, default: false },
|
|
47
53
|
taggable: { type: Boolean, default: true },
|
|
48
54
|
},
|
|
49
|
-
data
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
data() {
|
|
56
|
+
return {
|
|
57
|
+
isDirty: false,
|
|
58
|
+
isActive: false,
|
|
59
|
+
validationErrors: [],
|
|
60
|
+
generatedInputId: createComponentInstanceId(
|
|
61
|
+
resolveKebabComponentName(this.$options.name)
|
|
62
|
+
),
|
|
63
|
+
}
|
|
64
|
+
},
|
|
54
65
|
mounted() {
|
|
55
66
|
this.validate(this.resolvedValue)
|
|
56
67
|
},
|
|
57
68
|
|
|
58
69
|
computed: {
|
|
59
70
|
kebabComponentName() {
|
|
60
|
-
return
|
|
71
|
+
return resolveKebabComponentName(this.$options.name)
|
|
61
72
|
},
|
|
62
73
|
classes() {
|
|
63
74
|
let classes = {}
|
|
@@ -92,11 +103,7 @@ export default {
|
|
|
92
103
|
},
|
|
93
104
|
|
|
94
105
|
computedId() {
|
|
95
|
-
return this.$attrs.id
|
|
96
|
-
? this.$attrs.id
|
|
97
|
-
: `${this.kebabComponentName}_${Math.round(
|
|
98
|
-
Math.random() * 10000000000000000
|
|
99
|
-
)}`
|
|
106
|
+
return this.$attrs.id ? this.$attrs.id : this.generatedInputId
|
|
100
107
|
},
|
|
101
108
|
|
|
102
109
|
selectedItem() {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { getCurrentInstance, useId } from 'vue'
|
|
2
2
|
|
|
3
3
|
export default function createComponentInstanceId(prefix = 'asd20-component') {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
if (getCurrentInstance()) {
|
|
5
|
+
return `${prefix}-${useId()}`
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return `${prefix}-detached`
|
|
6
9
|
}
|