@tabler/core 1.6.0 → 1.6.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/dist/css/tabler-flags.css +1 -1
- package/dist/css/tabler-flags.css.map +1 -1
- package/dist/css/tabler-flags.min.css +1 -1
- package/dist/css/tabler-flags.min.css.map +1 -1
- package/dist/css/tabler-flags.rtl.css +1 -1
- package/dist/css/tabler-flags.rtl.css.map +1 -1
- package/dist/css/tabler-flags.rtl.min.css +1 -1
- package/dist/css/tabler-flags.rtl.min.css.map +1 -1
- package/dist/css/tabler-marketing.css +1 -1
- package/dist/css/tabler-marketing.css.map +1 -1
- package/dist/css/tabler-marketing.min.css +1 -1
- package/dist/css/tabler-marketing.min.css.map +1 -1
- package/dist/css/tabler-marketing.rtl.css +1 -1
- package/dist/css/tabler-marketing.rtl.css.map +1 -1
- package/dist/css/tabler-marketing.rtl.min.css +1 -1
- package/dist/css/tabler-marketing.rtl.min.css.map +1 -1
- package/dist/css/tabler-payments.css +1 -1
- package/dist/css/tabler-payments.css.map +1 -1
- package/dist/css/tabler-payments.min.css +1 -1
- package/dist/css/tabler-payments.min.css.map +1 -1
- package/dist/css/tabler-payments.rtl.css +1 -1
- package/dist/css/tabler-payments.rtl.css.map +1 -1
- package/dist/css/tabler-payments.rtl.min.css +1 -1
- package/dist/css/tabler-payments.rtl.min.css.map +1 -1
- package/dist/css/tabler-props.css +1 -1
- package/dist/css/tabler-props.css.map +1 -1
- package/dist/css/tabler-props.min.css +1 -1
- package/dist/css/tabler-props.min.css.map +1 -1
- package/dist/css/tabler-props.rtl.css +1 -1
- package/dist/css/tabler-props.rtl.css.map +1 -1
- package/dist/css/tabler-props.rtl.min.css +1 -1
- package/dist/css/tabler-props.rtl.min.css.map +1 -1
- package/dist/css/tabler-socials.css +1 -1
- package/dist/css/tabler-socials.css.map +1 -1
- package/dist/css/tabler-socials.min.css +1 -1
- package/dist/css/tabler-socials.min.css.map +1 -1
- package/dist/css/tabler-socials.rtl.css +1 -1
- package/dist/css/tabler-socials.rtl.css.map +1 -1
- package/dist/css/tabler-socials.rtl.min.css +1 -1
- package/dist/css/tabler-socials.rtl.min.css.map +1 -1
- package/dist/css/tabler-themes.css +1 -1
- package/dist/css/tabler-themes.css.map +1 -1
- package/dist/css/tabler-themes.min.css +1 -1
- package/dist/css/tabler-themes.min.css.map +1 -1
- package/dist/css/tabler-themes.rtl.css +1 -1
- package/dist/css/tabler-themes.rtl.css.map +1 -1
- package/dist/css/tabler-themes.rtl.min.css +1 -1
- package/dist/css/tabler-themes.rtl.min.css.map +1 -1
- package/dist/css/tabler-vendors.css +1 -1
- package/dist/css/tabler-vendors.css.map +1 -1
- package/dist/css/tabler-vendors.min.css +1 -1
- package/dist/css/tabler-vendors.min.css.map +1 -1
- package/dist/css/tabler-vendors.rtl.css +1 -1
- package/dist/css/tabler-vendors.rtl.css.map +1 -1
- package/dist/css/tabler-vendors.rtl.min.css +1 -1
- package/dist/css/tabler-vendors.rtl.min.css.map +1 -1
- package/dist/css/tabler.css +12 -2
- package/dist/css/tabler.css.map +1 -1
- package/dist/css/tabler.min.css +2 -2
- package/dist/css/tabler.min.css.map +1 -1
- package/dist/css/tabler.rtl.css +12 -2
- package/dist/css/tabler.rtl.css.map +1 -1
- package/dist/css/tabler.rtl.min.css +2 -2
- package/dist/css/tabler.rtl.min.css.map +1 -1
- package/dist/js/tabler-theme.esm.js +21 -6
- package/dist/js/tabler-theme.esm.js.map +1 -1
- package/dist/js/tabler-theme.esm.min.js +2 -2
- package/dist/js/tabler-theme.esm.min.js.map +1 -1
- package/dist/js/tabler-theme.js +21 -6
- package/dist/js/tabler-theme.js.map +1 -1
- package/dist/js/tabler-theme.min.js +2 -2
- package/dist/js/tabler-theme.min.js.map +1 -1
- package/dist/js/tabler.esm.js +332 -131
- package/dist/js/tabler.esm.js.map +1 -1
- package/dist/js/tabler.esm.min.js +2 -2
- package/dist/js/tabler.esm.min.js.map +1 -1
- package/dist/js/tabler.js +332 -131
- package/dist/js/tabler.js.map +1 -1
- package/dist/js/tabler.min.js +2 -2
- package/dist/js/tabler.min.js.map +1 -1
- package/dist/libs/vanilla-calendar-pro/index.js +2 -2
- package/dist/types/src/clipboard.d.ts +3 -0
- package/dist/types/src/confetti.d.ts +2 -0
- package/dist/types/src/countup.d.ts +2 -0
- package/dist/types/src/datepicker.d.ts +24 -6
- package/dist/types/src/input-mask.d.ts +2 -1
- package/dist/types/src/otp-input.d.ts +6 -2
- package/dist/types/src/strength.d.ts +1 -0
- package/dist/types/src/switch-icon.d.ts +1 -0
- package/js/src/autosize.ts +8 -3
- package/js/src/bootstrap/base-component.ts +12 -2
- package/js/src/bootstrap/dom/manipulator.ts +1 -1
- package/js/src/bootstrap/dropdown.ts +3 -1
- package/js/src/bootstrap/util/index.ts +14 -1
- package/js/src/clipboard.ts +26 -2
- package/js/src/confetti.ts +42 -7
- package/js/src/countup.ts +30 -12
- package/js/src/datepicker.ts +147 -27
- package/js/src/dropdown.ts +12 -5
- package/js/src/global.d.ts +4 -2
- package/js/src/input-mask.ts +7 -7
- package/js/src/otp-input.ts +99 -12
- package/js/src/popover.ts +13 -7
- package/js/src/sidebar.ts +14 -6
- package/js/src/sortable.ts +5 -1
- package/js/src/sparkline.ts +15 -6
- package/js/src/strength.ts +17 -4
- package/js/src/switch-icon.ts +15 -0
- package/js/src/tab.ts +17 -7
- package/js/src/toast.ts +19 -11
- package/js/src/tooltip.ts +16 -7
- package/js/tabler-theme.ts +28 -4
- package/package.json +2 -2
- package/scss/layout/_navbar.scss +1 -2
- package/scss/ui/_cards.scss +14 -2
package/js/src/autosize.ts
CHANGED
|
@@ -92,7 +92,7 @@ class Autosize extends BaseComponent {
|
|
|
92
92
|
// Public
|
|
93
93
|
update(): void {
|
|
94
94
|
const element = this._element
|
|
95
|
-
if (!element.isConnected || element.
|
|
95
|
+
if (!element.isConnected || element.getClientRects().length === 0) {
|
|
96
96
|
return
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -100,11 +100,16 @@ class Autosize extends BaseComponent {
|
|
|
100
100
|
// position is put back afterwards.
|
|
101
101
|
const { scrollTop } = document.documentElement
|
|
102
102
|
const style = getComputedStyle(element)
|
|
103
|
-
|
|
103
|
+
// scrollHeight is the padding box: a border-box field adds its borders, a
|
|
104
|
+
// content-box field drops its padding.
|
|
105
|
+
const extra = style.boxSizing === 'border-box' ? Number.parseFloat(style.borderTopWidth) + Number.parseFloat(style.borderBottomWidth) : -(Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom))
|
|
104
106
|
const before = element.style.height
|
|
105
107
|
|
|
108
|
+
// Measured without a scrollbar: one left over from the previous size
|
|
109
|
+
// would wrap the text and add a phantom line.
|
|
110
|
+
element.style.overflowY = 'hidden'
|
|
106
111
|
element.style.height = 'auto'
|
|
107
|
-
const height = `${element.scrollHeight +
|
|
112
|
+
const height = `${element.scrollHeight + extra}px`
|
|
108
113
|
element.style.height = height
|
|
109
114
|
|
|
110
115
|
// Past max-height the content no longer fits, so let the field scroll.
|
|
@@ -25,10 +25,12 @@ class BaseComponent extends Config {
|
|
|
25
25
|
return
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
const ctor = this.constructor as unknown as BaseComponentStatic
|
|
29
|
+
Data.get<BaseComponent>(resolved, ctor.DATA_KEY)?.dispose()
|
|
30
|
+
|
|
28
31
|
this._element = resolved
|
|
29
32
|
this._config = this._getConfig(config)
|
|
30
33
|
|
|
31
|
-
const ctor = this.constructor as unknown as BaseComponentStatic
|
|
32
34
|
Data.set(this._element, ctor.DATA_KEY, this)
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -43,7 +45,15 @@ class BaseComponent extends Config {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
_queueCallback(callback: () => void, element: HTMLElement, isAnimated = true): void {
|
|
46
|
-
executeAfterTransition(
|
|
48
|
+
executeAfterTransition(
|
|
49
|
+
() => {
|
|
50
|
+
if (this._element) {
|
|
51
|
+
callback()
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
element,
|
|
55
|
+
isAnimated,
|
|
56
|
+
)
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
_getConfig(config?: ComponentConfig): ComponentConfig {
|
|
@@ -43,7 +43,7 @@ const PREFIXES = ['tblr', 'bs'] as const
|
|
|
43
43
|
|
|
44
44
|
const Manipulator = {
|
|
45
45
|
setDataAttribute(element: HTMLElement, key: string, value: string): void {
|
|
46
|
-
element.setAttribute(`data-
|
|
46
|
+
element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)
|
|
47
47
|
},
|
|
48
48
|
|
|
49
49
|
removeDataAttribute(element: HTMLElement, key: string): void {
|
|
@@ -58,7 +58,9 @@ const CLASS_NAME_DROPUP_CENTER = 'dropup-center'
|
|
|
58
58
|
const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'
|
|
59
59
|
|
|
60
60
|
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled), [data-tblr-toggle="dropdown"]:not(.disabled):not(:disabled)'
|
|
61
|
-
const SELECTOR_DATA_TOGGLE_SHOWN =
|
|
61
|
+
const SELECTOR_DATA_TOGGLE_SHOWN = SELECTOR_DATA_TOGGLE.split(', ')
|
|
62
|
+
.map((selector) => `${selector}.${CLASS_NAME_SHOW}`)
|
|
63
|
+
.join(', ')
|
|
62
64
|
const SELECTOR_MENU = '.dropdown-menu'
|
|
63
65
|
const SELECTOR_NAVBAR = '.navbar'
|
|
64
66
|
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
|
|
@@ -62,7 +62,18 @@ const triggerTransitionEnd = (element: HTMLElement): void => {
|
|
|
62
62
|
element.dispatchEvent(new Event(TRANSITION_END))
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// A jQuery collection stands in for its first element, as in upstream Bootstrap.
|
|
66
|
+
const unwrapJQuery = (object: unknown): unknown => {
|
|
67
|
+
if (object && typeof object === 'object' && typeof (object as { jquery?: unknown }).jquery !== 'undefined') {
|
|
68
|
+
return (object as { 0?: unknown })[0]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return object
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
const isElement = (object: unknown): object is HTMLElement => {
|
|
75
|
+
object = unwrapJQuery(object)
|
|
76
|
+
|
|
66
77
|
if (!object || typeof object !== 'object') {
|
|
67
78
|
return false
|
|
68
79
|
}
|
|
@@ -71,6 +82,8 @@ const isElement = (object: unknown): object is HTMLElement => {
|
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
const getElement = (object: unknown): HTMLElement | null => {
|
|
85
|
+
object = unwrapJQuery(object)
|
|
86
|
+
|
|
74
87
|
if (isElement(object)) {
|
|
75
88
|
return object
|
|
76
89
|
}
|
|
@@ -159,7 +172,7 @@ const reflow = (element: HTMLElement): void => {
|
|
|
159
172
|
const isRTL = (): boolean => document.documentElement.dir === 'rtl'
|
|
160
173
|
|
|
161
174
|
const getjQuery = (): JQueryStaticLike | null => {
|
|
162
|
-
if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
|
|
175
|
+
if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery') && !document.body.hasAttribute('data-tblr-no-jquery')) {
|
|
163
176
|
return window.jQuery
|
|
164
177
|
}
|
|
165
178
|
|
package/js/src/clipboard.ts
CHANGED
|
@@ -9,6 +9,7 @@ import BaseComponent from './bootstrap/base-component'
|
|
|
9
9
|
import EventHandler from './bootstrap/dom/event-handler'
|
|
10
10
|
import SelectorEngine from './bootstrap/dom/selector-engine'
|
|
11
11
|
import { initAll } from './bootstrap/util/component-functions'
|
|
12
|
+
import { getElement } from './bootstrap/util/index'
|
|
12
13
|
import type { ElementSelector } from './bootstrap/types'
|
|
13
14
|
|
|
14
15
|
type ComponentConfig = {
|
|
@@ -38,6 +39,8 @@ const CLASS_NAME_COPIED = 'copied'
|
|
|
38
39
|
const SELECTOR_DATA_TOGGLE = `[data-bs-toggle="${NAME}"], [data-tblr-toggle="${NAME}"]`
|
|
39
40
|
const SELECTOR_LABEL = `.${NAME}-label`
|
|
40
41
|
const SELECTOR_FEEDBACK = `.${NAME}-feedback`
|
|
42
|
+
const ATTRIBUTE_TEXT = 'data-bs-text'
|
|
43
|
+
const ATTRIBUTE_TEXT_ALIAS = 'data-tblr-text'
|
|
41
44
|
|
|
42
45
|
const Default: ComponentConfig = {
|
|
43
46
|
target: null,
|
|
@@ -130,12 +133,18 @@ class Clipboard extends BaseComponent {
|
|
|
130
133
|
return String(text)
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
const source = target ?
|
|
136
|
+
const source = target ? getElement(target) : null
|
|
134
137
|
if (!source) {
|
|
135
138
|
return ''
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
if (source instanceof HTMLInputElement || source instanceof HTMLTextAreaElement) {
|
|
142
|
+
return source.value
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// innerText keeps line breaks and skips hidden text such as a
|
|
146
|
+
// visually-hidden label; textContent is the fallback for SVG.
|
|
147
|
+
return (source instanceof HTMLElement ? source.innerText : ((source as Element).textContent ?? '')).trim()
|
|
139
148
|
}
|
|
140
149
|
|
|
141
150
|
// Public
|
|
@@ -158,12 +167,27 @@ class Clipboard extends BaseComponent {
|
|
|
158
167
|
EventHandler.trigger(this._element, EVENT_COPIED)
|
|
159
168
|
}
|
|
160
169
|
|
|
170
|
+
/** Ends the copied state, for a `delay` of 0 or an early reset. */
|
|
171
|
+
reset(): void {
|
|
172
|
+
window.clearTimeout(this._timeout)
|
|
173
|
+
this._toggleCopied(false)
|
|
174
|
+
}
|
|
175
|
+
|
|
161
176
|
dispose(): void {
|
|
162
177
|
window.clearTimeout(this._timeout)
|
|
163
178
|
super.dispose()
|
|
164
179
|
}
|
|
165
180
|
|
|
166
181
|
// Private
|
|
182
|
+
_mergeConfigObj(config?: Record<string, unknown>, element?: HTMLElement): Record<string, unknown> {
|
|
183
|
+
// The text is copied as written: `data-bs-text="007"` or `"true"` must not
|
|
184
|
+
// be read as a number or a boolean the way other data attributes are.
|
|
185
|
+
const raw = element?.getAttribute(ATTRIBUTE_TEXT) ?? element?.getAttribute(ATTRIBUTE_TEXT_ALIAS)
|
|
186
|
+
const dataOptions: Record<string, unknown> = raw === null || raw === undefined ? {} : { text: raw }
|
|
187
|
+
|
|
188
|
+
return super._mergeConfigObj({ ...dataOptions, ...config }, element)
|
|
189
|
+
}
|
|
190
|
+
|
|
167
191
|
_showCopied(): void {
|
|
168
192
|
window.clearTimeout(this._timeout)
|
|
169
193
|
this._toggleCopied(true)
|
package/js/src/confetti.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import BaseComponent from './bootstrap/base-component'
|
|
9
9
|
import EventHandler from './bootstrap/dom/event-handler'
|
|
10
|
+
import Manipulator from './bootstrap/dom/manipulator'
|
|
10
11
|
import SelectorEngine from './bootstrap/dom/selector-engine'
|
|
11
12
|
import { isDisabled } from './bootstrap/util/index'
|
|
12
13
|
import type { ComponentConfig as BaseConfig, ElementSelector } from './bootstrap/types'
|
|
@@ -58,6 +59,8 @@ type Emitter = {
|
|
|
58
59
|
stopped: boolean
|
|
59
60
|
/** every piece has landed and `end` was fired */
|
|
60
61
|
done: boolean
|
|
62
|
+
/** the instance was disposed: the pieces still land, but `end` is not fired */
|
|
63
|
+
detached: boolean
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
/**
|
|
@@ -133,14 +136,21 @@ const stage = {
|
|
|
133
136
|
frame: 0,
|
|
134
137
|
lastTick: 0,
|
|
135
138
|
|
|
136
|
-
add(emitter: Emitter):
|
|
137
|
-
this.emitters.push(emitter)
|
|
139
|
+
add(emitter: Emitter): boolean {
|
|
138
140
|
this._mount()
|
|
139
141
|
|
|
142
|
+
if (!this.context) {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.emitters.push(emitter)
|
|
147
|
+
|
|
140
148
|
if (!this.frame) {
|
|
141
149
|
this.lastTick = performance.now()
|
|
142
150
|
this.frame = requestAnimationFrame((now) => this._tick(now))
|
|
143
151
|
}
|
|
152
|
+
|
|
153
|
+
return true
|
|
144
154
|
},
|
|
145
155
|
|
|
146
156
|
_mount(): void {
|
|
@@ -161,9 +171,14 @@ const stage = {
|
|
|
161
171
|
display: 'block',
|
|
162
172
|
})
|
|
163
173
|
|
|
174
|
+
const context = canvas.getContext('2d')
|
|
175
|
+
if (!context) {
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
164
179
|
document.body.append(canvas)
|
|
165
180
|
this.canvas = canvas
|
|
166
|
-
this.context =
|
|
181
|
+
this.context = context
|
|
167
182
|
this._resize()
|
|
168
183
|
window.addEventListener('resize', this._onResize)
|
|
169
184
|
},
|
|
@@ -191,7 +206,7 @@ const stage = {
|
|
|
191
206
|
// The backing store is scaled for HiDPI screens; the CSS size above keeps
|
|
192
207
|
// the element at the viewport size, so the drawing stays sharp and 1:1.
|
|
193
208
|
const dpr = Math.min(window.devicePixelRatio || 1, MAX_DPR)
|
|
194
|
-
this.canvas.width =
|
|
209
|
+
this.canvas.width = document.documentElement.clientWidth * dpr
|
|
195
210
|
this.canvas.height = window.innerHeight * dpr
|
|
196
211
|
this.context.setTransform(dpr, 0, 0, dpr, 0, 0)
|
|
197
212
|
},
|
|
@@ -312,7 +327,10 @@ const stage = {
|
|
|
312
327
|
|
|
313
328
|
this.emitters.splice(i, 1)
|
|
314
329
|
emitter.done = true
|
|
315
|
-
|
|
330
|
+
|
|
331
|
+
if (!emitter.detached) {
|
|
332
|
+
EventHandler.trigger(emitter.element, EVENT_END)
|
|
333
|
+
}
|
|
316
334
|
}
|
|
317
335
|
},
|
|
318
336
|
}
|
|
@@ -372,9 +390,13 @@ class Confetti extends BaseComponent {
|
|
|
372
390
|
emitted: 0,
|
|
373
391
|
stopped: false,
|
|
374
392
|
done: false,
|
|
393
|
+
detached: false,
|
|
375
394
|
}
|
|
376
395
|
|
|
377
|
-
stage.add(this._emitter)
|
|
396
|
+
if (!stage.add(this._emitter)) {
|
|
397
|
+
this._emitter = null
|
|
398
|
+
EventHandler.trigger(this._element, EVENT_END)
|
|
399
|
+
}
|
|
378
400
|
}
|
|
379
401
|
|
|
380
402
|
stop(): void {
|
|
@@ -386,6 +408,10 @@ class Confetti extends BaseComponent {
|
|
|
386
408
|
}
|
|
387
409
|
|
|
388
410
|
dispose(): void {
|
|
411
|
+
if (this._emitter) {
|
|
412
|
+
this._emitter.detached = true
|
|
413
|
+
}
|
|
414
|
+
|
|
389
415
|
this.stop()
|
|
390
416
|
super.dispose()
|
|
391
417
|
}
|
|
@@ -432,7 +458,16 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
|
|
432
458
|
}
|
|
433
459
|
|
|
434
460
|
const target = SelectorEngine.getElementFromSelector(this) || this
|
|
435
|
-
const
|
|
461
|
+
const config = Manipulator.getDataAttributes(this)
|
|
462
|
+
const instance = Confetti.getOrCreateInstance(target, config) as Confetti
|
|
463
|
+
|
|
464
|
+
// The instance lives on the target and keeps the config it was created
|
|
465
|
+
// with, so re-read the trigger's options on every click; several triggers
|
|
466
|
+
// can then share one target with their own look.
|
|
467
|
+
if (target !== this) {
|
|
468
|
+
instance._config = instance._getConfig(config) as ComponentConfig
|
|
469
|
+
}
|
|
470
|
+
|
|
436
471
|
instance.burst()
|
|
437
472
|
})
|
|
438
473
|
|
package/js/src/countup.ts
CHANGED
|
@@ -147,6 +147,7 @@ class CountUp extends BaseComponent {
|
|
|
147
147
|
_elapsed = 0
|
|
148
148
|
_running = false
|
|
149
149
|
_paused = false
|
|
150
|
+
_started = false
|
|
150
151
|
|
|
151
152
|
constructor(element: ElementSelector, config?: ComponentConfigInput) {
|
|
152
153
|
super(element, config)
|
|
@@ -175,13 +176,7 @@ class CountUp extends BaseComponent {
|
|
|
175
176
|
return
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
this._observer = new IntersectionObserver((entries) =>
|
|
179
|
-
if (entries.some((entry) => entry.isIntersecting)) {
|
|
180
|
-
this._observer?.disconnect()
|
|
181
|
-
this._observer = null
|
|
182
|
-
this.start()
|
|
183
|
-
}
|
|
184
|
-
})
|
|
179
|
+
this._observer = new IntersectionObserver((entries) => this._observerCallback(entries))
|
|
185
180
|
this._observer.observe(this._element)
|
|
186
181
|
}
|
|
187
182
|
|
|
@@ -205,6 +200,8 @@ class CountUp extends BaseComponent {
|
|
|
205
200
|
|
|
206
201
|
reset(): void {
|
|
207
202
|
this._stop()
|
|
203
|
+
// Lets the observer start the animation again when the element comes into view.
|
|
204
|
+
this._started = false
|
|
208
205
|
this._value = this._config.startVal
|
|
209
206
|
this._print(this._value)
|
|
210
207
|
}
|
|
@@ -244,7 +241,11 @@ class CountUp extends BaseComponent {
|
|
|
244
241
|
|
|
245
242
|
if (raw) {
|
|
246
243
|
try {
|
|
247
|
-
|
|
244
|
+
const parsed: unknown = JSON.parse(raw)
|
|
245
|
+
// Only an object carries options; `null`, a number or an array is ignored.
|
|
246
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
247
|
+
dataOptions = parsed as Record<string, unknown>
|
|
248
|
+
}
|
|
248
249
|
} catch {
|
|
249
250
|
// ignore invalid JSON
|
|
250
251
|
}
|
|
@@ -253,8 +254,21 @@ class CountUp extends BaseComponent {
|
|
|
253
254
|
return super._mergeConfigObj({ ...coerceOptions(dataOptions), ...config }, element)
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
_observerCallback(entries: IntersectionObserverEntry[]): void {
|
|
258
|
+
if (entries.some((entry) => entry.isIntersecting)) {
|
|
259
|
+
this._observer?.disconnect()
|
|
260
|
+
this._observer = null
|
|
261
|
+
|
|
262
|
+
// update() may have already animated it while it was off screen.
|
|
263
|
+
if (!this._started) {
|
|
264
|
+
this.start()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
256
269
|
_animate(from: number, to: number): void {
|
|
257
270
|
this._stop()
|
|
271
|
+
this._started = true
|
|
258
272
|
|
|
259
273
|
// Users who asked for less motion get the final number at once.
|
|
260
274
|
if (from === to || this._config.duration <= 0 || reducedMotion()) {
|
|
@@ -317,14 +331,18 @@ class CountUp extends BaseComponent {
|
|
|
317
331
|
}
|
|
318
332
|
|
|
319
333
|
if (format === 'time') {
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
// Rounded away from zero on both sides, so -90.5 and 90.5 give the same
|
|
335
|
+
// minutes; a value that rounds to 0 stays unsigned (-0 < 0 is false).
|
|
336
|
+
const rounded = Math.sign(value) * Math.round(Math.abs(value))
|
|
337
|
+
const minutes = Math.abs(rounded)
|
|
338
|
+
return `${rounded < 0 ? '-' : ''}${prefix}${Math.floor(minutes / 60)}:${String(minutes % 60).padStart(2, '0')}${suffix}`
|
|
322
339
|
}
|
|
323
340
|
|
|
324
|
-
const
|
|
341
|
+
const fixed = value.toFixed(decimalPlaces)
|
|
342
|
+
const [integer, fraction] = fixed.replace('-', '').split('.') as [string, string | undefined]
|
|
325
343
|
const grouped = useGrouping ? integer.replace(/\B(?=(\d{3})+(?!\d))/g, separator) : integer
|
|
326
344
|
|
|
327
|
-
return `${
|
|
345
|
+
return `${Number(fixed) < 0 ? '-' : ''}${prefix}${grouped}${fraction ? decimal + fraction : ''}${suffix}`
|
|
328
346
|
}
|
|
329
347
|
}
|
|
330
348
|
|