bt-core-app 0.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/.vscode/extensions.json +3 -0
- package/README.md +45 -0
- package/index.html +13 -0
- package/package.json +39 -0
- package/public/vite.svg +1 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/BT-Btn.vue +40 -0
- package/src/components/BT-Col.vue +36 -0
- package/src/components/BT-Span.vue +21 -0
- package/src/components/Dialog-Confirm.vue +47 -0
- package/src/components/Dialog-Select-Date.vue +89 -0
- package/src/components/Dialog-Select.vue +140 -0
- package/src/components/Dialog-Text.vue +59 -0
- package/src/composables/actions-tracker.ts +99 -0
- package/src/composables/actions.ts +354 -0
- package/src/composables/api.ts +471 -0
- package/src/composables/auth.ts +382 -0
- package/src/composables/cosmetics.ts +178 -0
- package/src/composables/csv.ts +198 -0
- package/src/composables/dates.ts +79 -0
- package/src/composables/demo.ts +25 -0
- package/src/composables/dialogs.ts +115 -0
- package/src/composables/document-meta.ts +40 -0
- package/src/composables/draggable.ts +189 -0
- package/src/composables/filters.ts +256 -0
- package/src/composables/forage.ts +49 -0
- package/src/composables/helpers.ts +694 -0
- package/src/composables/id.ts +20 -0
- package/src/composables/list.ts +601 -0
- package/src/composables/navigation.ts +214 -0
- package/src/composables/presets.ts +20 -0
- package/src/composables/pwa.ts +89 -0
- package/src/composables/resizable.ts +382 -0
- package/src/composables/rules.ts +40 -0
- package/src/composables/stores.ts +797 -0
- package/src/composables/track.ts +55 -0
- package/src/composables/urls.ts +11 -0
- package/src/core.ts +92 -0
- package/src/index.ts +16 -0
- package/src/types.ts +13 -0
- package/src/useApi.ts +68 -0
- package/src/vite-env.d.ts +1 -0
- package/test/api.test.ts +84 -0
- package/test/forage.test.ts +31 -0
- package/test/helpers.test.ts +231 -0
- package/test/navigation.test.ts +99 -0
- package/test/stores-last-update.test.ts +138 -0
- package/test/stores-session.test.ts +118 -0
- package/test/track.test.ts +29 -0
- package/test/utils.ts +15 -0
- package/tsconfig.json +33 -0
- package/tsconfig.node.json +11 -0
- package/vite.config.ts +19 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
//nav item needs to have a 'ignore Suspension' prop
|
|
2
|
+
import { useUrl } from '@/composables/urls'
|
|
3
|
+
import { appendUrl, deepSelect } from '@/composables/helpers'
|
|
4
|
+
import { ref, type Ref } from 'vue'
|
|
5
|
+
import { type AuthItem } from './auth'
|
|
6
|
+
|
|
7
|
+
export interface ExternalParty {
|
|
8
|
+
party?: string
|
|
9
|
+
property?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ExternalNavigation {
|
|
13
|
+
canPull?: boolean
|
|
14
|
+
canPush?: boolean
|
|
15
|
+
convertFunc?: Function
|
|
16
|
+
name?: string
|
|
17
|
+
localNavigation?: string
|
|
18
|
+
localDisplayPath?: string
|
|
19
|
+
localComparePath?: string
|
|
20
|
+
syncComparePath?: string
|
|
21
|
+
syncDisplayPath?: string
|
|
22
|
+
syncIDPath?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface NavigationItem extends AuthItem {
|
|
26
|
+
/**aliases are other names that could use this navigation item's set of permissions, etc.*/
|
|
27
|
+
aliases?: string[]
|
|
28
|
+
/**potentially the nav that leads to the archive */
|
|
29
|
+
archiveName?: string
|
|
30
|
+
/**the name of the background img to show when the route opens this nav */
|
|
31
|
+
background?: string
|
|
32
|
+
/**how long until locally cached data is refreshed */
|
|
33
|
+
cacheExpiryHours?: number
|
|
34
|
+
/**any children of this nav item */
|
|
35
|
+
children?: NavigationItem[]
|
|
36
|
+
/**the name that will be displayed in the navigation menu */
|
|
37
|
+
displayName?: string
|
|
38
|
+
/**where credentials can be found for external parties*/
|
|
39
|
+
externalPartyCredentialNavigation?: string
|
|
40
|
+
/**possible external parties to connect to and sync with */
|
|
41
|
+
externalParties?: ExternalParty[]
|
|
42
|
+
/**possible external party navigation items and how to connect/sync */
|
|
43
|
+
externalNavigations?: ExternalNavigation[]
|
|
44
|
+
/**will hide app bar if set to true when the route opens this nav */
|
|
45
|
+
hideAppBar?: boolean
|
|
46
|
+
/**will hide the nav sidebar if set to true when the route opens this nav */
|
|
47
|
+
hideNavigation?: boolean
|
|
48
|
+
/**will open a dialog box to confirm navigating away from this route */
|
|
49
|
+
hesitate?: boolean
|
|
50
|
+
/**the mdi icon that goes with this nav item. Will show in places like the nav menu */
|
|
51
|
+
icon?: string
|
|
52
|
+
/**default to false. True will mean that even is account is suspended, this will still allow navigation */
|
|
53
|
+
ignoreSuspension?: boolean
|
|
54
|
+
/**default to true. When false will hide from nav menu */
|
|
55
|
+
isInNavMenu?: boolean
|
|
56
|
+
/**the microservice the leads to the default url to obtain data from. Defaults to 'default'. */
|
|
57
|
+
microservice?: string
|
|
58
|
+
/**the name of this nav item. */
|
|
59
|
+
name?: string
|
|
60
|
+
/**the url path on top of the base microservice url */
|
|
61
|
+
path?: string
|
|
62
|
+
/**permissions that are required for this navItem. All these permissions must be met. */
|
|
63
|
+
permissions?: string[]
|
|
64
|
+
/**default to true. When false will allow universal access regardless of permission */
|
|
65
|
+
requiresAuth?: boolean
|
|
66
|
+
/**the name of the route to access individual items */
|
|
67
|
+
singleName?: string
|
|
68
|
+
/**this nav item is restricted to these subscriptions. So the user needs to have at least one of these subscription codes. */
|
|
69
|
+
subscriptions?: string[]
|
|
70
|
+
/**this nav item is preferred in this subscription codes. */
|
|
71
|
+
subFilters?: string[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const appBar = ref(true)
|
|
75
|
+
const appNavigation = ref(false)
|
|
76
|
+
const backgroundName: Ref<string | undefined> = ref()
|
|
77
|
+
const hesitate = ref(false)
|
|
78
|
+
let removeHesitateListener: any = null
|
|
79
|
+
|
|
80
|
+
export interface BTNavigation {
|
|
81
|
+
showAppBar: Ref<boolean>
|
|
82
|
+
showAppNavigation: Ref<boolean>
|
|
83
|
+
backgroundName: Ref<string | undefined>
|
|
84
|
+
hesitate: Ref<boolean>
|
|
85
|
+
navigationItems: NavigationItem[],
|
|
86
|
+
findArchiveName: (navName?: string) => string | undefined
|
|
87
|
+
findCacheHours: (navName?: string) => number
|
|
88
|
+
findDisplay: (navName?: string) => string | undefined
|
|
89
|
+
findIcon: (navName?: string) => string | undefined
|
|
90
|
+
findItem: (navName?: string | NavigationItem) => NavigationItem | null
|
|
91
|
+
findStoreName: (navName?: string) => string
|
|
92
|
+
findPath: (navName?: string) => string | undefined
|
|
93
|
+
findSingleDisplay: (navName?: string) => string | undefined
|
|
94
|
+
updateNavigationProperties: (navName?: string | NavigationItem) => void
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface UseNavigationOptions {
|
|
98
|
+
defaultCacheExpiryHours?: number
|
|
99
|
+
navItems?: NavigationItem[]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function createNavigation(options: UseNavigationOptions): BTNavigation {
|
|
103
|
+
const cacheExpiryHours = options.defaultCacheExpiryHours ?? 7
|
|
104
|
+
const navigationList = options.navItems ?? []
|
|
105
|
+
|
|
106
|
+
function findArchiveName(navName?: string): string | undefined {
|
|
107
|
+
return findItem(navName)?.archiveName
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function findCacheHours(navName?: string): number {
|
|
111
|
+
return findItem(navName)?.cacheExpiryHours ?? cacheExpiryHours
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function findDisplay(navName?: string): string | undefined {
|
|
115
|
+
return findItem(navName)?.displayName
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function findIcon(navName?: string): string | undefined {
|
|
119
|
+
return findItem(navName)?.icon
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function findItem(navName?: string | NavigationItem) {
|
|
123
|
+
if (navName == null) return null
|
|
124
|
+
if (typeof navName != 'string') return navName
|
|
125
|
+
|
|
126
|
+
const items = deepSelect(navigationList, (x: NavigationItem) => x.children) as NavigationItem[]
|
|
127
|
+
|
|
128
|
+
return items.find(navItem => navItem.name == navName ||
|
|
129
|
+
navItem.singleName == navName ||
|
|
130
|
+
navItem.aliases?.some(y => y == navName)) ?? null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**defaults to microservice of default */
|
|
134
|
+
function findPath(navName?: string) {
|
|
135
|
+
const navItem = findItem(navName)
|
|
136
|
+
if (navItem == null) return undefined
|
|
137
|
+
|
|
138
|
+
let vPath = useUrl(navItem.microservice ?? 'default') ?? ''
|
|
139
|
+
|
|
140
|
+
if (navItem.path != null)
|
|
141
|
+
vPath = appendUrl(vPath, navItem.path)
|
|
142
|
+
|
|
143
|
+
if (vPath?.endsWith('/'))
|
|
144
|
+
vPath = vPath.slice(0, vPath.length - 1)
|
|
145
|
+
|
|
146
|
+
return vPath
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**finds display name and attempts to remove plural suffixes */
|
|
150
|
+
function findSingleDisplay(navName?: string): string | undefined {
|
|
151
|
+
const item = findItem(navName)
|
|
152
|
+
|
|
153
|
+
if (item?.singleName != null)
|
|
154
|
+
return item.singleName
|
|
155
|
+
|
|
156
|
+
const displayName = item?.displayName
|
|
157
|
+
|
|
158
|
+
if (displayName == null) return undefined
|
|
159
|
+
|
|
160
|
+
if (displayName.endsWith('ies'))
|
|
161
|
+
return displayName.slice(0, displayName.length - 3)
|
|
162
|
+
|
|
163
|
+
if (displayName.endsWith('es'))
|
|
164
|
+
return displayName.slice(0, displayName.length - 2)
|
|
165
|
+
|
|
166
|
+
if (displayName.endsWith('s'))
|
|
167
|
+
return displayName.slice(0, displayName.length - 1)
|
|
168
|
+
|
|
169
|
+
return displayName
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function findStoreName(navName?: string) {
|
|
173
|
+
const navItem = findItem(navName)
|
|
174
|
+
return navItem?.name ?? navItem?.singleName ?? 'store'
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**updates background, navigation sidebar, and app bar settings */
|
|
178
|
+
function updateNavigationProperties(navName?: string | NavigationItem) {
|
|
179
|
+
if (navName == null) return
|
|
180
|
+
|
|
181
|
+
const item = typeof navName == 'string' ? findItem(navName) : navName
|
|
182
|
+
|
|
183
|
+
backgroundName.value = item?.background
|
|
184
|
+
appNavigation.value = item?.hideNavigation !== true
|
|
185
|
+
appBar.value = item?.hideAppBar !== true
|
|
186
|
+
|
|
187
|
+
if (removeHesitateListener != null)
|
|
188
|
+
removeHesitateListener()
|
|
189
|
+
|
|
190
|
+
hesitate.value = item?.hesitate === true
|
|
191
|
+
|
|
192
|
+
if (hesitate.value)
|
|
193
|
+
removeHesitateListener = window.addEventListener('beforeunload', e => {
|
|
194
|
+
e.preventDefault()
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
showAppBar: appBar,
|
|
200
|
+
showAppNavigation: appNavigation,
|
|
201
|
+
backgroundName,
|
|
202
|
+
hesitate,
|
|
203
|
+
navigationItems: navigationList,
|
|
204
|
+
findArchiveName,
|
|
205
|
+
findCacheHours,
|
|
206
|
+
findDisplay,
|
|
207
|
+
findIcon,
|
|
208
|
+
findItem,
|
|
209
|
+
findStoreName,
|
|
210
|
+
findPath,
|
|
211
|
+
findSingleDisplay,
|
|
212
|
+
updateNavigationProperties
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface BTPresets {
|
|
2
|
+
usePresets(preset?: string): any
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface CreatePresetsOptions {
|
|
6
|
+
presets: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createPresets(options: CreatePresetsOptions): BTPresets {
|
|
10
|
+
|
|
11
|
+
function usePresets(preset?: string): any {
|
|
12
|
+
if (!preset) return {}
|
|
13
|
+
let mPreset = preset as keyof typeof options.presets
|
|
14
|
+
return options.presets[mPreset] ?? {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
usePresets
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { tryOnMounted } from '@vueuse/core'
|
|
2
|
+
import { ref, type Ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
export interface SWEvent extends Event {
|
|
5
|
+
detail: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let updateListener: void | undefined
|
|
9
|
+
let controllerChangeListener: void | undefined
|
|
10
|
+
let promptListener: void | undefined
|
|
11
|
+
let isUpdating = false
|
|
12
|
+
|
|
13
|
+
const canInstallApp = ref(false)
|
|
14
|
+
const canUpdateApp = ref(false)
|
|
15
|
+
const prompt: Ref<any> = ref()
|
|
16
|
+
const sWorker: Ref<any> = ref()
|
|
17
|
+
|
|
18
|
+
export interface BTPWA {
|
|
19
|
+
canInstallApp: Ref<boolean>
|
|
20
|
+
canUpdateApp: Ref<boolean>
|
|
21
|
+
installApp: () => void
|
|
22
|
+
isInstalled: () => boolean
|
|
23
|
+
updateApp: () => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createPWA(): BTPWA {
|
|
27
|
+
|
|
28
|
+
function notifyUpdateAvailable(e: any) {
|
|
29
|
+
sWorker.value = e.detail
|
|
30
|
+
canUpdateApp.value = true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function controllerChanged() {
|
|
34
|
+
if (isUpdating) return
|
|
35
|
+
isUpdating = true
|
|
36
|
+
window.location.reload()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function installApp() {
|
|
40
|
+
prompt.value?.prompt()
|
|
41
|
+
canInstallApp.value = false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isInstalled() {
|
|
45
|
+
// For iOS
|
|
46
|
+
// if (window.navigator.standalone) return true
|
|
47
|
+
|
|
48
|
+
// For Android
|
|
49
|
+
if (window.matchMedia('(display-mode: standalone)').matches) return true
|
|
50
|
+
|
|
51
|
+
// If neither is true, it's not installed
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function storePrompt(e: Event) {
|
|
56
|
+
e.preventDefault()
|
|
57
|
+
canInstallApp.value = true
|
|
58
|
+
prompt.value = e
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function updateApp() {
|
|
62
|
+
canUpdateApp.value = false
|
|
63
|
+
|
|
64
|
+
if (!sWorker.value || !sWorker.value.waiting)
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
sWorker.value.waiting.postMessage({ type: 'SKIP_WAITING' })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
tryOnMounted(() => {
|
|
71
|
+
if (updateListener == null) {
|
|
72
|
+
updateListener = document.addEventListener('swUpdated', notifyUpdateAvailable, { once: true })
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (controllerChangeListener == null)
|
|
76
|
+
controllerChangeListener = navigator.serviceWorker.addEventListener('controllerchange', controllerChanged)
|
|
77
|
+
|
|
78
|
+
if (promptListener == null)
|
|
79
|
+
promptListener = window.addEventListener('beforeinstallprompt', storePrompt)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
canInstallApp,
|
|
84
|
+
canUpdateApp,
|
|
85
|
+
installApp,
|
|
86
|
+
isInstalled,
|
|
87
|
+
updateApp
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { toValue, useEventListener, useParentElement } from "@vueuse/core"
|
|
2
|
+
import { type ComponentPublicInstance, type Ref, ref } from 'vue'
|
|
3
|
+
import type { Position, MaybeRefOrGetter } from "@vueuse/core"
|
|
4
|
+
import { copyDeep } from "@/composables/helpers"
|
|
5
|
+
import { type BladeVariant } from "@/types"
|
|
6
|
+
|
|
7
|
+
export type ResizeHandle = 't' | 'r' | 'b' | 'l' | 'tr' | 'br' | 'bl' | 'tl'
|
|
8
|
+
|
|
9
|
+
export interface PointerOrTouchEvent extends PointerEvent, TouchEvent { }
|
|
10
|
+
|
|
11
|
+
const allHandles: ResizeHandle[] = ['t', 'r', 'b', 'l', 'tr', 'br', 'bl', 'tl']
|
|
12
|
+
|
|
13
|
+
export interface UseResizeOptions {
|
|
14
|
+
preventDefault?: MaybeRefOrGetter<boolean>
|
|
15
|
+
stopPropagation?: MaybeRefOrGetter<boolean>
|
|
16
|
+
// pointerTypes?: PointerType[]
|
|
17
|
+
handles?: ResizeHandle[]
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Callback when the dragging starts. Return `false` to prevent dragging.
|
|
21
|
+
*/
|
|
22
|
+
onStart?: (position: Position, event: PointerEvent) => void | false
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Callback during dragging.
|
|
26
|
+
*/
|
|
27
|
+
onMove?: (position: Position, event: PointerEvent) => void
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Callback when dragging end.
|
|
31
|
+
*/
|
|
32
|
+
onEnd?: (position: Position, event: PointerEvent) => void
|
|
33
|
+
|
|
34
|
+
minWidth?: number
|
|
35
|
+
maxWidth?: number
|
|
36
|
+
minHeight?: number
|
|
37
|
+
maxHeight?: number
|
|
38
|
+
handleWidth?: number
|
|
39
|
+
handleZIndex?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ElementPosition {
|
|
43
|
+
left?: number,
|
|
44
|
+
top?: number,
|
|
45
|
+
height?: number,
|
|
46
|
+
width?: number,
|
|
47
|
+
position?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function useResizable(
|
|
51
|
+
target: MaybeRefOrGetter<ComponentPublicInstance | null>,
|
|
52
|
+
options: UseResizeOptions = {}) {
|
|
53
|
+
let currentVariant: string | undefined = undefined
|
|
54
|
+
let handleWidth = options.handleWidth ?? 12
|
|
55
|
+
let handleZIndex = options.handleZIndex ?? 100
|
|
56
|
+
let minWidth = options.minWidth ?? 0
|
|
57
|
+
let maxWidth = options.maxWidth ?? Number.MAX_SAFE_INTEGER
|
|
58
|
+
let minHeight = options.minHeight ?? 0
|
|
59
|
+
let maxHeight = options.maxHeight ?? Number.MAX_SAFE_INTEGER
|
|
60
|
+
let preventDefault = options.preventDefault ?? false
|
|
61
|
+
let stopPropagation = options.stopPropagation ?? false
|
|
62
|
+
|
|
63
|
+
let activeHandle: any
|
|
64
|
+
let handleEls: HTMLDivElement[] = []
|
|
65
|
+
const isAutoResizing = ref(false)
|
|
66
|
+
let listeners: Function[] = []
|
|
67
|
+
let moveListeners: Function[] = []
|
|
68
|
+
const resizingIsOn: Ref<boolean> = ref(false)
|
|
69
|
+
const startingPointerPosition: Ref<Position> = ref<Position>({ x: 0, y: 0 })
|
|
70
|
+
let startingElementPosition: ElementPosition = { left: 0, top: 0, height: 0, width: 0, position: 'absolute' }
|
|
71
|
+
let currentElementPosition: ElementPosition = { }
|
|
72
|
+
// let variantMemory: any = {}
|
|
73
|
+
|
|
74
|
+
function handleEvent(e: PointerOrTouchEvent) {
|
|
75
|
+
if (toValue(preventDefault))
|
|
76
|
+
e.preventDefault()
|
|
77
|
+
if (toValue(stopPropagation))
|
|
78
|
+
e.stopPropagation()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function addHandles(el: ComponentPublicInstance, handles: ResizeHandle[]) {
|
|
82
|
+
handleEls = handles.map(createHandleEl)
|
|
83
|
+
handleEls.forEach(handleEl => el.$el.appendChild(handleEl))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function removeHandles(el: ComponentPublicInstance) {
|
|
87
|
+
handleEls.forEach(handleEl => el.$el.removeChild(handleEl))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function createHandleEl(handle: string): HTMLDivElement { //ComponentPublicInstance {
|
|
91
|
+
const handleWidthPx = handleWidth + 'px'
|
|
92
|
+
const handleOffsetPx = -handleWidth / 2 + 'px'
|
|
93
|
+
const handleCornerZIndex = handleZIndex + 1
|
|
94
|
+
|
|
95
|
+
const handleEl = document.createElement('div')
|
|
96
|
+
handleEl.dataset.handle = handle
|
|
97
|
+
handleEl.style.position = 'absolute'
|
|
98
|
+
handleEl.style.touchAction = 'none'
|
|
99
|
+
handleEl.style.userSelect = 'none'
|
|
100
|
+
handleEl.style.zIndex = (handle.length === 1 ? handleZIndex : handleCornerZIndex).toString()
|
|
101
|
+
handleEl.style.cursor = getCursor(handle)
|
|
102
|
+
|
|
103
|
+
if (handle.includes('t')) {
|
|
104
|
+
handleEl.style.top = handleOffsetPx
|
|
105
|
+
handleEl.style.height = handleWidthPx
|
|
106
|
+
if (handle === 't') {
|
|
107
|
+
handleEl.style.left = '0'
|
|
108
|
+
handleEl.style.width = '100%'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (handle.includes('b')) {
|
|
112
|
+
handleEl.style.bottom = handleOffsetPx
|
|
113
|
+
handleEl.style.height = handleWidthPx
|
|
114
|
+
if (handle === 'b') {
|
|
115
|
+
handleEl.style.left = '0'
|
|
116
|
+
handleEl.style.width = '100%'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (handle.includes('r')) {
|
|
120
|
+
handleEl.style.right = handleOffsetPx
|
|
121
|
+
handleEl.style.width = handleWidthPx
|
|
122
|
+
if (handle === 'r') {
|
|
123
|
+
handleEl.style.top = '0'
|
|
124
|
+
handleEl.style.height = '100%'
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (handle.includes('l')) {
|
|
128
|
+
handleEl.style.left = handleOffsetPx
|
|
129
|
+
handleEl.style.width = handleWidthPx
|
|
130
|
+
if (handle === 'l') {
|
|
131
|
+
handleEl.style.top = '0'
|
|
132
|
+
handleEl.style.height = '100%'
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return handleEl
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function start(e: PointerOrTouchEvent) {
|
|
139
|
+
if (!handleEls.some(x => x == e.target)) return
|
|
140
|
+
const t = toValue(target)
|
|
141
|
+
if (!t) return
|
|
142
|
+
const el = t.$el
|
|
143
|
+
const isTouch = e.type === 'touchstart' && e.touches.length > 0
|
|
144
|
+
const evtData = isTouch ? e.touches[0] : e
|
|
145
|
+
|
|
146
|
+
startingPointerPosition.value.x = evtData.clientX
|
|
147
|
+
startingPointerPosition.value.y = evtData.clientY
|
|
148
|
+
|
|
149
|
+
startingElementPosition = {
|
|
150
|
+
left: el.offsetLeft,
|
|
151
|
+
top: el.offsetTop,
|
|
152
|
+
height: parseInt(window.getComputedStyle(el).getPropertyValue('height')),
|
|
153
|
+
width: parseInt(window.getComputedStyle(el).getPropertyValue('width'))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
currentElementPosition = copyDeep(startingElementPosition)
|
|
157
|
+
|
|
158
|
+
if (options.onStart?.(startingPointerPosition.value, e) === false) return
|
|
159
|
+
|
|
160
|
+
let handle = e.target as HTMLElement
|
|
161
|
+
|
|
162
|
+
document.documentElement.style.cursor = getCursor(handle.dataset.handle as string)
|
|
163
|
+
activeHandle = handle.dataset.handle as string
|
|
164
|
+
|
|
165
|
+
moveListeners.push(useEventListener('mousemove', move))
|
|
166
|
+
moveListeners.push(useEventListener('mouseup', end))
|
|
167
|
+
moveListeners.push(useEventListener('touchmove', move))
|
|
168
|
+
moveListeners.push(useEventListener('touchend', end))
|
|
169
|
+
|
|
170
|
+
handleEvent(e)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function move(e: any) {
|
|
174
|
+
const t = toValue(target)
|
|
175
|
+
if (!t) return
|
|
176
|
+
const el = t.$el
|
|
177
|
+
|
|
178
|
+
const isTouch = e.type === 'touchmove' && e.touches.length > 0
|
|
179
|
+
const evtData = isTouch ? e.touches[0] : e
|
|
180
|
+
let dx = evtData.clientX - startingPointerPosition.value.x
|
|
181
|
+
let dy = evtData.clientY - startingPointerPosition.value.y
|
|
182
|
+
const start = toValue(startingElementPosition)
|
|
183
|
+
|
|
184
|
+
if (activeHandle.includes('t')) {
|
|
185
|
+
const newHeight = Math.min(maxHeight, Math.max(minHeight, (start.height ?? 0) - dy))
|
|
186
|
+
currentElementPosition.height = newHeight
|
|
187
|
+
currentElementPosition.top = (start.top ?? 0) + dy
|
|
188
|
+
}
|
|
189
|
+
if (activeHandle.includes('b')) {
|
|
190
|
+
const newHeight = Math.min(maxHeight, Math.max(minHeight, (start.height ?? 0) + dy))
|
|
191
|
+
currentElementPosition.height = newHeight
|
|
192
|
+
}
|
|
193
|
+
if (activeHandle.includes('l')) {
|
|
194
|
+
const newWidth = Math.min(maxWidth, Math.max(minWidth, (start.width ?? 0) - dx))
|
|
195
|
+
currentElementPosition.width = newWidth
|
|
196
|
+
currentElementPosition.left = (start.left ?? 0) + dx
|
|
197
|
+
}
|
|
198
|
+
if (activeHandle.includes('r')) {
|
|
199
|
+
const newWidth = Math.min(maxWidth, Math.max(minWidth, (start.width ?? 0) + dx))
|
|
200
|
+
currentElementPosition.width = newWidth
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
el.dispatchEvent(new CustomEvent('resize'))
|
|
204
|
+
handleEvent(e)
|
|
205
|
+
|
|
206
|
+
applyToElementStyle(currentElementPosition)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function end() {
|
|
210
|
+
document.documentElement.style.cursor = ''
|
|
211
|
+
activeHandle = null
|
|
212
|
+
moveListeners.forEach(l => { l() })
|
|
213
|
+
moveListeners.length = 0
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function getCursor(handle: string) {
|
|
217
|
+
const cursorDirection: any = {
|
|
218
|
+
t: 'n',
|
|
219
|
+
r: 'e',
|
|
220
|
+
b: 's',
|
|
221
|
+
l: 'w'
|
|
222
|
+
}
|
|
223
|
+
return (handle.split('').map(l => cursorDirection[l]).join('') + '-resize')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function restoreSize(variant?: BladeVariant | undefined) {
|
|
227
|
+
const t = toValue(target)
|
|
228
|
+
if (!t) return
|
|
229
|
+
const el = t.$el as HTMLElement
|
|
230
|
+
console.log(currentVariant)
|
|
231
|
+
|
|
232
|
+
if (currentVariant !== 'page' && variant == 'page') {
|
|
233
|
+
//moving back to default page settings
|
|
234
|
+
const parent = useParentElement(el)
|
|
235
|
+
const p = (toValue(parent) ?? null) as any
|
|
236
|
+
let aimWidth = 0
|
|
237
|
+
if (p != null) {
|
|
238
|
+
const c = window.getComputedStyle(p)
|
|
239
|
+
aimWidth = parseInt(c.getPropertyValue('width')) - p.offsetLeft
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
el.style.transition = 'width 0.5s, height 0.5s, top 0.5s, left 0.5s'
|
|
243
|
+
el.offsetHeight
|
|
244
|
+
isAutoResizing.value = true
|
|
245
|
+
|
|
246
|
+
applyToElementStyle({
|
|
247
|
+
height: undefined,
|
|
248
|
+
width: aimWidth,
|
|
249
|
+
top: 0,
|
|
250
|
+
left: 0,
|
|
251
|
+
position: undefined
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
useEventListener(el, 'transitionend', () => {
|
|
255
|
+
el.style.width = 'auto'
|
|
256
|
+
el.style.top = ''
|
|
257
|
+
el.style.left = ''
|
|
258
|
+
el.style.position = ''
|
|
259
|
+
el.style.transition = ''
|
|
260
|
+
isAutoResizing.value = false
|
|
261
|
+
}, { once: true })
|
|
262
|
+
}
|
|
263
|
+
else if ((currentVariant == 'page' || t.$el.style.position !== 'absolute') && variant !== 'page') {
|
|
264
|
+
//move to freestyle or blade
|
|
265
|
+
el.style.transition = 'width 0.5s, height 0.5s, top 0.5s, left 0.5s'
|
|
266
|
+
isAutoResizing.value = true
|
|
267
|
+
|
|
268
|
+
startingElementPosition = {
|
|
269
|
+
height: parseInt(window.getComputedStyle(el).getPropertyValue('height')),
|
|
270
|
+
width: parseInt(window.getComputedStyle(el).getPropertyValue('width')),
|
|
271
|
+
top: el.offsetTop,
|
|
272
|
+
left: el.offsetLeft,
|
|
273
|
+
position: 'absolute'
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
applyToElementStyle(startingElementPosition)
|
|
277
|
+
el.offsetHeight
|
|
278
|
+
const s = startingElementPosition
|
|
279
|
+
|
|
280
|
+
if (variant == 'freestyle') {
|
|
281
|
+
//half the size to indicate non-page variant
|
|
282
|
+
currentElementPosition = {
|
|
283
|
+
height: s.height,
|
|
284
|
+
width: (s.width ?? 100) / 2,
|
|
285
|
+
top: (s.top ?? 0) + 2,
|
|
286
|
+
left: (s.left ?? 0) + 2,
|
|
287
|
+
position: 'absolute'
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else if (variant == 'blade') {
|
|
291
|
+
currentElementPosition = {
|
|
292
|
+
height: s.height,
|
|
293
|
+
width: (s.width ?? 100) / 2,
|
|
294
|
+
top: (s.top ?? 0) + 2,
|
|
295
|
+
left: (s.left ?? 0) + 2,
|
|
296
|
+
position: 'absolute'
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
//flex column?
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
applyToElementStyle(currentElementPosition)
|
|
303
|
+
|
|
304
|
+
useEventListener(el, 'transitionend', () => {
|
|
305
|
+
el.style.transition = ''
|
|
306
|
+
isAutoResizing.value = false
|
|
307
|
+
}, { once: true })
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function applyToElementStyle(s: ElementPosition) {
|
|
312
|
+
const t = toValue(target)
|
|
313
|
+
if (!t) return
|
|
314
|
+
const el = t.$el
|
|
315
|
+
|
|
316
|
+
if (s.top) {
|
|
317
|
+
el.style.top = `${s.top}px`
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
el.style.top = undefined
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (s.left) {
|
|
324
|
+
el.style.left = `${s.left}px`
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
el.style.left = undefined
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (s.height) {
|
|
331
|
+
el.style.height = `${s.height}px`
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
el.style.height = undefined
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (s.width) {
|
|
338
|
+
el.style.width = `${s.width}px`
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
el.style.width = undefined
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (s.position) {
|
|
345
|
+
el.style.position = `${s.position}`
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
el.style.position = undefined
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
console.log(el.style.position)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function turnResizingOn(handles?: ResizeHandle[], variant?: BladeVariant) {
|
|
355
|
+
if (resizingIsOn.value) return
|
|
356
|
+
const t = toValue(target)
|
|
357
|
+
if (!t) return
|
|
358
|
+
addHandles(t, handles ?? allHandles)
|
|
359
|
+
restoreSize(variant)
|
|
360
|
+
listeners.push(useEventListener(t.$el, 'mousedown', start))
|
|
361
|
+
listeners.push(useEventListener(t.$el, 'touchstart', start))
|
|
362
|
+
resizingIsOn.value = true
|
|
363
|
+
currentVariant = variant
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function turnResizingOff(variant?: BladeVariant | undefined) {
|
|
367
|
+
if (!resizingIsOn.value) return
|
|
368
|
+
const t = toValue(target)
|
|
369
|
+
if (!t) return
|
|
370
|
+
removeHandles(t)
|
|
371
|
+
restoreSize(variant)
|
|
372
|
+
listeners.forEach(l => { l() })
|
|
373
|
+
listeners.length = 0
|
|
374
|
+
resizingIsOn.value = false
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
resizingIsOn,
|
|
379
|
+
turnResizingOn,
|
|
380
|
+
turnResizingOff
|
|
381
|
+
}
|
|
382
|
+
}
|