Skip to content

Commit 45482f9

Browse files
authored
Merge pull request #7231 from LibreSign/chore/migration-to-vue3
chore: migration to vue3
2 parents 0371a2b + 5a0f6a1 commit 45482f9

File tree

17 files changed

+941
-984
lines changed

17 files changed

+941
-984
lines changed

src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
</template>
2424

2525
<script setup lang="ts">
26-
import { ref, computed, defineOptions } from 'vue'
26+
import { ref, computed } from 'vue'
2727
2828
defineOptions({ name: 'LibreSign' })
2929

src/ExternalApp.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</template>
1313

1414
<script setup lang="ts">
15-
import { computed, defineOptions } from 'vue'
15+
import { computed } from 'vue'
1616
1717
defineOptions({ name: 'LibreSignExternal' })
1818

src/composables/useFileEntry.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { computed } from '@vue/reactivity'
7+
8+
import { useSidebarStore } from '../store/sidebar.js'
9+
10+
type FileEntryMetadata = {
11+
extension?: string
12+
}
13+
14+
export type FileEntrySource = {
15+
id: number
16+
name: string
17+
nodeType?: string
18+
created_at?: number | string | Date | null
19+
metadata?: FileEntryMetadata
20+
[key: string]: unknown
21+
}
22+
23+
type FileEntryStore = {
24+
selectFile: (id: number) => void
25+
}
26+
27+
type ActionsMenuStore = {
28+
opened: number | null
29+
}
30+
31+
type FileEntryProps = {
32+
source: FileEntrySource
33+
}
34+
35+
export function useFileEntry(
36+
props: FileEntryProps,
37+
options: {
38+
actionsMenuStore: ActionsMenuStore
39+
filesStore: FileEntryStore
40+
},
41+
) {
42+
const sidebarStore = useSidebarStore()
43+
44+
const mtime = computed(() => new Date(props.source?.created_at || Date.now()))
45+
const openedMenu = computed({
46+
get: () => options.actionsMenuStore.opened === props.source.id,
47+
set: (opened: boolean) => {
48+
options.actionsMenuStore.opened = opened ? props.source.id : null
49+
},
50+
})
51+
const mtimeOpacity = computed(() => {
52+
const maxOpacityTime = 31 * 24 * 60 * 60 * 1000
53+
const timestamp = mtime.value?.getTime?.()
54+
55+
if (!timestamp) {
56+
return {}
57+
}
58+
59+
const ratio = Math.round(Math.min(100, 100 * (maxOpacityTime - (Date.now() - timestamp)) / maxOpacityTime))
60+
if (ratio < 0) {
61+
return {}
62+
}
63+
64+
return {
65+
color: `color-mix(in srgb, var(--color-main-text) ${ratio}%, var(--color-text-maxcontrast))`,
66+
}
67+
})
68+
const fileExtension = computed(() => {
69+
if (props.source.nodeType === 'envelope') {
70+
return ''
71+
}
72+
return props.source.metadata?.extension ? `.${props.source.metadata.extension}` : '.pdf'
73+
})
74+
75+
function onRightClick(event: MouseEvent) {
76+
if (openedMenu.value) {
77+
return
78+
}
79+
80+
options.actionsMenuStore.opened = props.source.id
81+
event.preventDefault()
82+
event.stopPropagation()
83+
84+
const target = event.currentTarget as HTMLElement | null
85+
const root = target?.closest('.app-content') as HTMLElement | null
86+
if (!root) {
87+
return
88+
}
89+
90+
const contentRect = root.getBoundingClientRect()
91+
root.style.setProperty('--mouse-pos-x', `${Math.max(0, event.clientX - contentRect.left - 200)}px`)
92+
root.style.setProperty('--mouse-pos-y', `${Math.max(0, event.clientY - contentRect.top)}px`)
93+
}
94+
95+
function openDetailsIfAvailable(event: Event) {
96+
event.preventDefault()
97+
event.stopPropagation()
98+
options.filesStore.selectFile(props.source.id)
99+
sidebarStore.activeRequestSignatureTab()
100+
}
101+
102+
return {
103+
mtime,
104+
openedMenu,
105+
mtimeOpacity,
106+
fileExtension,
107+
onRightClick,
108+
openDetailsIfAvailable,
109+
}
110+
}

src/mixins/isTouchDevice.js

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/mixins/signingOrderMixin.js

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/tests/composables/useIsTouchDevice.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@ describe('useIsTouchDevice composable', () => {
1919

2020
expect(isTouchDevice.value).toBe(expected)
2121
})
22+
23+
it('keeps the same value across repeated reads', () => {
24+
const { isTouchDevice } = useIsTouchDevice()
25+
26+
expect(isTouchDevice.value).toBe(isTouchDevice.value)
27+
})
2228
})

src/tests/composables/useSigningOrder.spec.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,121 @@ describe('useSigningOrder composable', () => {
2323
recalculateSigningOrders(signers, 0)
2424
expect(signers[0].signingOrder).toBe(1)
2525
})
26+
27+
it('increments following signers when inserting at start', () => {
28+
const signers: Signer[] = [
29+
{ signingOrder: 0 },
30+
{ signingOrder: 1 },
31+
{ signingOrder: 2 },
32+
]
33+
34+
recalculateSigningOrders(signers, 0)
35+
36+
expect(signers[0].signingOrder).toBe(1)
37+
expect(signers[1].signingOrder).toBe(2)
38+
expect(signers[2].signingOrder).toBe(3)
39+
})
40+
41+
it('assigns next order when adding at end', () => {
42+
const signers: Signer[] = [
43+
{ signingOrder: 1 },
44+
{ signingOrder: 2 },
45+
]
46+
47+
recalculateSigningOrders(signers, 1)
48+
49+
expect(signers[1].signingOrder).toBe(2)
50+
})
51+
52+
it('handles reordering with original orders when moving forward', () => {
53+
const signers: Signer[] = [
54+
{ signingOrder: 1 },
55+
{ signingOrder: 2 },
56+
{ signingOrder: 3 },
57+
]
58+
59+
recalculateSigningOrders(signers, 2, [1, 2, 3], 0)
60+
61+
expect(signers[2].signingOrder).toBeGreaterThanOrEqual(1)
62+
})
63+
64+
it('handles reordering with original orders when moving backward', () => {
65+
const signers: Signer[] = [
66+
{ signingOrder: 1 },
67+
{ signingOrder: 2 },
68+
{ signingOrder: 3 },
69+
]
70+
71+
recalculateSigningOrders(signers, 1, [1, 2, 3], 2)
72+
73+
expect(signers[1].signingOrder).toBeGreaterThanOrEqual(1)
74+
})
75+
76+
it('handles equal previous and next orders', () => {
77+
const signers: Signer[] = [
78+
{ signingOrder: 1 },
79+
{ signingOrder: 1 },
80+
{ signingOrder: 1 },
81+
]
82+
83+
recalculateSigningOrders(signers, 1)
84+
85+
expect(signers[1].signingOrder).toBeGreaterThanOrEqual(1)
86+
})
87+
88+
it('handles descending orders', () => {
89+
const signers: Signer[] = [
90+
{ signingOrder: 3 },
91+
{ signingOrder: 2 },
92+
{ signingOrder: 1 },
93+
]
94+
95+
recalculateSigningOrders(signers, 1)
96+
97+
expect(signers[1].signingOrder).toBeGreaterThanOrEqual(1)
98+
})
2699
})
27100

28101
describe('normalizeSigningOrders', () => {
102+
it('handles empty signers array', () => {
103+
const signers: Signer[] = []
104+
105+
normalizeSigningOrders(signers)
106+
107+
expect(signers).toEqual([])
108+
})
109+
29110
it('normalizes to start at 1', () => {
30111
const signers: Signer[] = [{ signingOrder: 5 }, { signingOrder: 6 }]
31112
normalizeSigningOrders(signers)
32113
expect(signers[0].signingOrder).toBe(1)
33114
expect(signers[1].signingOrder).toBe(2)
34115
})
116+
117+
it('handles negative orders', () => {
118+
const signers: Signer[] = [
119+
{ signingOrder: -1 },
120+
{ signingOrder: 0 },
121+
]
122+
123+
normalizeSigningOrders(signers)
124+
125+
expect(signers[0].signingOrder).toBe(1)
126+
expect(signers[1].signingOrder).toBe(2)
127+
})
128+
129+
it('closes gaps in the sequence', () => {
130+
const signers: Signer[] = [
131+
{ signingOrder: 1 },
132+
{ signingOrder: 5 },
133+
{ signingOrder: 10 },
134+
]
135+
136+
normalizeSigningOrders(signers)
137+
138+
expect(signers[0].signingOrder).toBe(1)
139+
expect(signers[1].signingOrder).toBe(2)
140+
expect(signers[2].signingOrder).toBe(3)
141+
})
35142
})
36143
})

0 commit comments

Comments
 (0)