@peckadesign/pd-naja 1.5.2 → 2.0.0
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/README.md +34 -0
- package/dist/PdNaja.esm.js +296 -53
- package/dist/PdNaja.esm.js.map +1 -1
- package/dist/{utils → classes}/ControlManager.d.ts +5 -3
- package/dist/classes/Suggest.d.ts +34 -0
- package/dist/extensions/BtnSpinnerExtension.d.ts +5 -9
- package/dist/extensions/ScrollToExtension.d.ts +16 -0
- package/dist/extensions/SpinnerExtension.d.ts +5 -7
- package/dist/extensions/SuggestExtension.d.ts +18 -0
- package/dist/extensions/ToggleClassExtension.d.ts +19 -0
- package/dist/index.esm.d.ts +6 -3
- package/dist/index.esm.js +296 -53
- package/dist/index.esm.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/utils.d.ts +3 -0
- package/package.json +2 -4
- package/src/{utils → classes}/ControlManager.ts +10 -8
- package/src/classes/Suggest.ts +215 -0
- package/src/extensions/BtnSpinnerExtension.ts +10 -32
- package/src/extensions/ScrollToExtension.ts +48 -0
- package/src/extensions/SpinnerExtension.ts +9 -24
- package/src/extensions/SuggestExtension.ts +66 -0
- package/src/extensions/ToggleClassExtension.ts +61 -0
- package/src/extensions/ViewTransitionExtension.txt +32 -0
- package/src/index.esm.ts +6 -4
- package/src/{utils/Control.ts → types.ts} +9 -2
- package/src/utils.ts +22 -0
- package/dist/utils/Control.d.ts +0 -4
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { InteractionEvent } from 'naja/dist/core/UIHandler'
|
|
2
|
+
import { CompleteEvent, Extension, Naja, StartEvent } from 'naja/dist/Naja'
|
|
3
|
+
import { Suggest } from '../classes/Suggest'
|
|
4
|
+
import { SpinnerPropsFn, SpinnerType } from '../types'
|
|
5
|
+
|
|
6
|
+
declare module 'naja/dist/Naja' {
|
|
7
|
+
interface Options {
|
|
8
|
+
suggest?: Suggest
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class SuggestExtension implements Extension {
|
|
13
|
+
private requestQueue: Set<Request> = new Set()
|
|
14
|
+
|
|
15
|
+
public readonly spinner: SpinnerType | undefined
|
|
16
|
+
public readonly getSpinnerProps?: SpinnerPropsFn
|
|
17
|
+
|
|
18
|
+
public constructor(spinner: SpinnerType | undefined = undefined, getSpinnerProps: SpinnerPropsFn = undefined) {
|
|
19
|
+
this.spinner = spinner
|
|
20
|
+
this.getSpinnerProps = getSpinnerProps
|
|
21
|
+
|
|
22
|
+
const forms = document.querySelectorAll<HTMLFormElement>(`.${Suggest.className}`)
|
|
23
|
+
|
|
24
|
+
forms.forEach((form) => {
|
|
25
|
+
new Suggest(form, {}, spinner, getSpinnerProps)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public initialize(naja: Naja): void {
|
|
30
|
+
naja.uiHandler.addEventListener('interaction', this.checkExtensionEnabled.bind(this))
|
|
31
|
+
naja.addEventListener('start', this.start.bind(this))
|
|
32
|
+
naja.addEventListener('complete', this.complete.bind(this))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private checkExtensionEnabled(event: InteractionEvent): void {
|
|
36
|
+
const { element, options } = event.detail
|
|
37
|
+
|
|
38
|
+
const inputElement = element as HTMLInputElement
|
|
39
|
+
if (inputElement.form && inputElement.form._suggest) {
|
|
40
|
+
options.suggest = inputElement.form._suggest
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private start(event: StartEvent): void {
|
|
45
|
+
const { options, request } = event.detail
|
|
46
|
+
|
|
47
|
+
if (options.suggest) {
|
|
48
|
+
this.requestQueue.add(request)
|
|
49
|
+
options.suggest.startSuggest()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private complete(event: CompleteEvent): void {
|
|
54
|
+
const { options, request } = event.detail
|
|
55
|
+
|
|
56
|
+
if (!options.suggest) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.requestQueue.delete(request)
|
|
61
|
+
|
|
62
|
+
if (this.requestQueue.size === 0) {
|
|
63
|
+
options.suggest.finishSuggest()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { InteractionEvent } from 'naja/dist/core/UIHandler'
|
|
2
|
+
import { CompleteEvent, Extension, Naja, StartEvent } from 'naja/dist/Naja'
|
|
3
|
+
|
|
4
|
+
type ToggleClassRecord = Record<string, string>
|
|
5
|
+
|
|
6
|
+
type ToggleClassOptions = {
|
|
7
|
+
element: Element
|
|
8
|
+
toggleClass: ToggleClassRecord
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module 'naja/dist/Naja' {
|
|
12
|
+
interface Options {
|
|
13
|
+
toggleClassOptions?: ToggleClassOptions
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ToggleClassExtension implements Extension {
|
|
18
|
+
public initialize(naja: Naja) {
|
|
19
|
+
naja.uiHandler.addEventListener('interaction', this.checkExtensionEnabled.bind(this))
|
|
20
|
+
naja.addEventListener('start', this.start.bind(this))
|
|
21
|
+
naja.addEventListener('complete', this.complete.bind(this))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private checkExtensionEnabled(event: InteractionEvent): void {
|
|
25
|
+
const { element, options } = event.detail
|
|
26
|
+
const toggleClass = JSON.parse(element.getAttribute('data-naja-toggle-class') || String(null))
|
|
27
|
+
|
|
28
|
+
if (toggleClass) {
|
|
29
|
+
options.toggleClassOptions = {
|
|
30
|
+
element,
|
|
31
|
+
toggleClass
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private start(event: StartEvent): void {
|
|
37
|
+
const { options } = event.detail
|
|
38
|
+
|
|
39
|
+
if (options.toggleClassOptions) {
|
|
40
|
+
this.applyToggleClass(options.toggleClassOptions)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private complete(event: CompleteEvent): void {
|
|
45
|
+
const { error, options } = event.detail
|
|
46
|
+
|
|
47
|
+
if (error && options.toggleClassOptions) {
|
|
48
|
+
this.applyToggleClass(options.toggleClassOptions)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private applyToggleClass(toggleClassOptions: ToggleClassOptions): void {
|
|
53
|
+
for (const [selector, classNames] of Object.entries(toggleClassOptions.toggleClass)) {
|
|
54
|
+
const targets = toggleClassOptions.element.querySelectorAll(selector)
|
|
55
|
+
|
|
56
|
+
targets.forEach((target) => {
|
|
57
|
+
classNames.split(' ').forEach((className) => target.classList.toggle(className))
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Extension, Naja } from 'naja/dist/Naja'
|
|
2
|
+
import { BeforeUpdateEvent } from 'naja/dist/core/SnippetHandler'
|
|
3
|
+
|
|
4
|
+
type SnippetUpdateOperation = (snippet: Element, content: string) => void
|
|
5
|
+
|
|
6
|
+
export class ViewTransitionExtension implements Extension {
|
|
7
|
+
private operation: SnippetUpdateOperation | undefined
|
|
8
|
+
|
|
9
|
+
public initialize(naja: Naja): void {
|
|
10
|
+
naja.snippetHandler.addEventListener('beforeUpdate', this.handleBeforeUpdate.bind(this))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private handleBeforeUpdate(event: BeforeUpdateEvent): void {
|
|
14
|
+
const { operation, changeOperation } = event.detail
|
|
15
|
+
|
|
16
|
+
if (document.startViewTransition !== undefined) {
|
|
17
|
+
this.operation = operation
|
|
18
|
+
changeOperation(this.replace.bind(this))
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public replace(snippet: Element, content: string): ViewTransition {
|
|
23
|
+
return document.startViewTransition(() => {
|
|
24
|
+
if (!this.operation) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.operation(snippet, content)
|
|
29
|
+
this.operation = undefined
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.esm.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
export { ControlManager } from './classes/ControlManager'
|
|
2
|
+
export { Suggest } from './classes/Suggest'
|
|
2
3
|
|
|
3
4
|
export { AjaxModalExtension } from './extensions/AjaxModalExtension'
|
|
4
5
|
export { AjaxModalPreventRedrawExtension } from './extensions/AjaxModalPreventRedrawExtension'
|
|
@@ -8,10 +9,11 @@ export { ConfirmExtension } from './extensions/ConfirmExtension'
|
|
|
8
9
|
export { FollowUpRequestExtension } from './extensions/FollowUpRequestExtension'
|
|
9
10
|
export { ForceRedirectExtension } from './extensions/ForceRedirectExtension'
|
|
10
11
|
export { ForceReplaceExtension } from './extensions/ForceReplaceExtension'
|
|
12
|
+
export { ScrollToExtension } from './extensions/ScrollToExtension'
|
|
11
13
|
export { SingleSubmitExtension } from './extensions/SingleSubmitExtension'
|
|
12
14
|
export { SnippetFormPartExtension } from './extensions/SnippetFormPartExtension'
|
|
13
15
|
export { SpinnerExtension } from './extensions/SpinnerExtension'
|
|
16
|
+
export { SuggestExtension } from './extensions/SuggestExtension'
|
|
17
|
+
export { ToggleClassExtension } from './extensions/ToggleClassExtension'
|
|
14
18
|
|
|
15
|
-
export { isDatasetTruthy,
|
|
16
|
-
|
|
17
|
-
export const controlManager = new ControlManager()
|
|
19
|
+
export { isDatasetFalsy, isDatasetTruthy, showSpinner, hideSpinner } from './utils'
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export type SpinnerType = ((props?: any) => Element) | Element
|
|
2
|
+
export type SpinnerPropsFn = ((initiator: Element) => any) | undefined
|
|
3
|
+
|
|
4
|
+
export interface WithSpinner {
|
|
5
|
+
spinner: SpinnerType
|
|
6
|
+
getSpinnerProps: SpinnerPropsFn
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
// `Control` is meant to be used for standalone components, that might be dependent on ajax. It should be used together
|
|
2
10
|
// with ControlManager. Class implementing the `Control` interface should export its instance. Then the intended
|
|
3
11
|
// lifecycle of class is as follows:
|
|
@@ -16,8 +24,7 @@
|
|
|
16
24
|
//
|
|
17
25
|
// 2. The `initialize` method is called for each snippet. It is called immediately after the snippet has been
|
|
18
26
|
// updated. The `context` argument is equal to the modified nette snippet.
|
|
19
|
-
|
|
20
|
-
export default interface Control {
|
|
27
|
+
export interface Control {
|
|
21
28
|
initialize(context: Element | Document): void
|
|
22
29
|
|
|
23
30
|
destroy?(context: Element): void
|
package/src/utils.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
import { WithSpinner } from './types'
|
|
2
|
+
|
|
3
|
+
export function showSpinner(this: WithSpinner, target: Element, initiator: Element = target): Element {
|
|
4
|
+
let spinner: Element
|
|
5
|
+
|
|
6
|
+
if (typeof this.spinner === 'function') {
|
|
7
|
+
spinner = this.getSpinnerProps ? this.spinner(this.getSpinnerProps(initiator)) : this.spinner()
|
|
8
|
+
} else {
|
|
9
|
+
spinner = this.spinner
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
target.appendChild(spinner)
|
|
13
|
+
spinner.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 100 })
|
|
14
|
+
|
|
15
|
+
return spinner
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function hideSpinner(spinner: Element): void {
|
|
19
|
+
const animation = spinner.animate({ opacity: 0 }, { duration: 100 })
|
|
20
|
+
animation.finished.then(() => spinner?.remove())
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
export const isDatasetTruthy = (element: Element, datasetName: string): boolean => {
|
|
2
24
|
const datasetValue = (element as HTMLElement).dataset[datasetName]
|
|
3
25
|
|
package/dist/utils/Control.d.ts
DELETED