Skip to content

Commit

Permalink
feat!: signature-text and signature-draw now support v-model file
Browse files Browse the repository at this point in the history
Signature Text and Signature Draw now support v-model both File object and base64-dataURI. And default v-model now is File

Migration:
change to every `v-model` to `v-model.base64`
  • Loading branch information
adenvt committed Jan 19, 2023
1 parent 97e520d commit 7a7ac5f
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 61 deletions.
29 changes: 2 additions & 27 deletions src/components/cropper/Cropper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,10 @@ import { useClamp } from '@vueuse/math'
import {
computed,
defineComponent,
onBeforeUnmount,
onMounted,
PropType,
ref,
StyleValue,
toRef,
watch,
} from 'vue-demi'
import { usePinch } from './utils/use-pinch'
import pButton from '../button/Button.vue'
Expand All @@ -141,7 +138,7 @@ import {
useRatioWidth,
} from './utils/use-ratio'
import { pAspectRatio } from '../aspect-ratio'
import { createSpinner } from '../avatar/utils/create-image'
import { usePreview } from './utils/use-preview'
export default defineComponent({
directives: { pAspectRatio },
Expand Down Expand Up @@ -223,12 +220,12 @@ export default defineComponent({
const y = ref(0)
const angle = ref(0)
const scale = useClamp(1, 0.5, 2)
const preview = ref(createSpinner(512, 512))
const ratio = useRatio(props)
const width = useRatioWidth(props)
const height = useRatioHeight(props)
const rounded = toRef(props, 'rounded')
const src = toRef(props, 'src')
const preview = usePreview(src)
const canvas = templateRef<HTMLCanvasElement>('canvas')
const parent = templateRef<HTMLDivElement>('parent')
const target = templateRef<HTMLImageElement>('target')
Expand Down Expand Up @@ -368,15 +365,6 @@ export default defineComponent({
},
})
watch(src, (value) => {
if (preview.value && preview.value.startsWith('blob'))
window.URL.revokeObjectURL(preview.value)
preview.value = value instanceof globalThis.File
? window.URL.createObjectURL(value)
: (value ?? createSpinner(512, 512))
})
watchDebounced([
src,
width,
Expand All @@ -392,19 +380,6 @@ export default defineComponent({
crop()
}, { debounce: 500 })
onMounted(() => {
if (props.src) {
preview.value = props.src instanceof globalThis.File
? window.URL.createObjectURL(props.src)
: props.src
}
})
onBeforeUnmount(() => {
if (preview.value && preview.value.startsWith('blob'))
window.URL.revokeObjectURL(preview.value)
})
return {
classNames,
preview,
Expand Down
42 changes: 42 additions & 0 deletions src/components/cropper/utils/use-preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createSpinner } from '../../avatar/utils/create-image'
import {
MaybeRef,
tryOnMounted,
tryOnBeforeUnmount,
} from '@vueuse/core'
import {
Ref,
ref,
unref,
watch,
} from 'vue-demi'

export function usePreview (file: MaybeRef<string | globalThis.File>, fallback = createSpinner(512, 512)): Ref<string> {
const preview = ref(fallback)

watch(() => unref(file), (value) => {
if (preview.value?.startsWith('blob'))
window.URL.revokeObjectURL(preview.value)

preview.value = value instanceof globalThis.File
? window.URL.createObjectURL(value)
: (value ?? fallback)
})

tryOnMounted(() => {
const value = unref(file)

if (value) {
preview.value = value instanceof globalThis.File
? window.URL.createObjectURL(value)
: value
}
})

tryOnBeforeUnmount(() => {
if (preview.value?.startsWith('blob'))
window.URL.revokeObjectURL(preview.value)
})

return preview
}
5 changes: 5 additions & 0 deletions src/components/signature-draw/SignatureDraw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ vi.mock('@vueuse/core', async () => {
}
})

vi.spyOn(window.URL, 'createObjectURL')
.mockImplementation((file: File) => {
return `blob://${file.name}`
})

afterEach(() => {
vi.resetAllMocks()
})
Expand Down
14 changes: 12 additions & 2 deletions src/components/signature-draw/SignatureDraw.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<component
:is="view"
v-model="model"
:model-modifiers="modelModifiers"
:width="width"
:height="height"
:color="color"
Expand All @@ -13,17 +14,26 @@

<script lang="ts">
import { useMediaQuery } from '@vueuse/core'
import { computed, defineComponent } from 'vue-demi'
import {
computed,
defineComponent,
PropType,
} from 'vue-demi'
import { useVModel } from '../input'
import SignatureDrawMobile from './SignatureDrawMobile.vue'
import SignatureDrawDesktop from './SignatureDrawDesktop.vue'
import { ModelModifier } from '../dropzone'
export default defineComponent({
props: {
modelValue: {
type : String,
type : [String, globalThis.File],
default: '',
},
modelModifiers: {
type : Object as PropType<ModelModifier>,
default: () => ({} as ModelModifier),
},
width: {
type : Number,
default: 430,
Expand Down
9 changes: 8 additions & 1 deletion src/components/signature-draw/SignatureDrawDesktop.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ vi.mock('./utils/use-draw.ts', () => ({ default: useDraw }))

vi.mock('./utils/canvas.ts', () => canvas)

beforeEach(() => {
vi.spyOn(window.URL, 'createObjectURL')
.mockImplementation((file: File) => {
return `blob://${file.name}`
})
})

afterEach(() => {
vi.restoreAllMocks()
})
Expand Down Expand Up @@ -165,7 +172,7 @@ it('should modify state in v-model', async () => {
const model = ref('')
const screen = render({
components: { SignatureDrawDesktop },
template : '<SignatureDrawDesktop v-model="model" />',
template : '<SignatureDrawDesktop v-model.base64="model" />',
setup () {
return { model }
},
Expand Down
20 changes: 16 additions & 4 deletions src/components/signature-draw/SignatureDrawDesktop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { templateRef } from '@vueuse/core'
import {
defineComponent,
onMounted,
PropType,
ref,
watch,
} from 'vue-demi'
Expand All @@ -50,14 +51,21 @@ import { useVModel } from '../input'
import { createLines } from './utils/smooth-line'
import Caption from '../caption/Caption.vue'
import Button from '../button/Button.vue'
import { ModelModifier } from '../dropzone'
import { usePreview } from '../cropper/utils/use-preview'
import { fromBase64 } from '../utils/base64'
export default defineComponent({
components: { Caption, Button },
props : {
modelValue: {
type : String,
type : [String, globalThis.File],
default: '',
},
modelModifiers: {
type : Object as PropType<ModelModifier>,
default: () => ({} as ModelModifier),
},
width: {
type : Number,
default: 430,
Expand Down Expand Up @@ -86,6 +94,7 @@ export default defineComponent({
emits: ['update:modelValue'],
setup (props) {
const model = useVModel(props)
const preview = usePreview(model, '')
const isBlank = ref(true)
const canvas = templateRef<HTMLCanvasElement>('canvas')
Expand Down Expand Up @@ -121,14 +130,17 @@ export default defineComponent({
drawLine(canvas.value, coordinate, options)
}
const result = toDataURL(canvas.value)
const value = props.modelModifiers.base64 ? result : fromBase64(result)
isBlank.value = false
model.value = toDataURL(canvas.value)
model.value = value
},
})
onMounted(() => {
if (model.value && model.value.startsWith('data:image/png')) {
placeImage(canvas.value, model.value)
if (model.value && preview.value) {
placeImage(canvas.value, preview.value)
isBlank.value = false
}
Expand Down
9 changes: 8 additions & 1 deletion src/components/signature-draw/SignatureDrawMobile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ vi.mock('@vueuse/core', async () => {

beforeEach(() => {
setWindow(375, 667)

vi.spyOn(window.URL, 'createObjectURL')
.mockImplementation((file: File) => {
return `blob://${file.name}`
})
})

afterEach(() => {
Expand Down Expand Up @@ -56,6 +61,7 @@ it('should open modal drawing if button `click to draw` clicked', async () => {
expect(open).toBeInTheDocument()

await fireEvent.click(open)
await delay(0)

const modal = screen.queryByTestId('signature-draw-modal')
const drawCanvas = screen.queryByTestId('signature-draw-desktop')
Expand All @@ -81,6 +87,7 @@ it('should go to rotate mode if screen width too small', async () => {
expect(draw).not.toHaveClass('signature-draw--normal')

await fireEvent.click(open)
await delay(0)

const desktop = screen.queryByTestId('signature-draw-desktop')
const drawCanvas = screen.queryByTestId('signature-draw-canvas')
Expand Down Expand Up @@ -143,7 +150,7 @@ it('should modify state in v-model', async () => {
const model = ref('')
const screen = render({
components: { SignatureDrawMobile },
template : '<SignatureDrawMobile v-model="model" />',
template : '<SignatureDrawMobile v-model.base64="model" />',
setup () {
return { model }
},
Expand Down
33 changes: 24 additions & 9 deletions src/components/signature-draw/SignatureDrawMobile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div class="signature-draw__preview">
<template v-if="model">
<img
:src="model"
:src="preview"
alt="signature-draw-preview"
data-testid="signature-draw-preview"
@click="open()">
Expand All @@ -25,7 +25,7 @@
<template v-else>
<Button
data-testid="signature-draw-open"
@click="open">
@click="open()">
{{ openDrawLabel }}
</Button>
</template>
Expand All @@ -36,7 +36,7 @@
class="signature-draw__modal"
data-testid="signature-draw-modal">
<SignatureDrawDesktop
v-model="rawModel"
v-model.base64="rawModel"
:class="classNames"
:placeholder="placeholder"
:color="color"
Expand All @@ -45,8 +45,9 @@
:reset-label="resetLabel" />
<Button
class="signature-draw__close"
nganu="1311"
data-testid="signature-draw-close"
@click="close">
@click="close()">
<span>{{ closeDrawLabel }}</span>
</Button>
</div>
Expand All @@ -59,6 +60,7 @@ import { useWindowSize } from '@vueuse/core'
import {
computed,
defineComponent,
PropType,
ref,
StyleValue,
watch,
Expand All @@ -69,6 +71,9 @@ import SignatureDrawDesktop from './SignatureDrawDesktop.vue'
import rotateImage from './utils/rotate-image'
import IconEdit from '@carbon/icons-vue/lib/edit/20'
import { pAspectRatio } from '../aspect-ratio'
import { ModelModifier } from '../dropzone'
import { usePreview } from '../cropper/utils/use-preview'
import { fromBase64 } from '../utils/base64'
export default defineComponent({
components: {
Expand All @@ -79,9 +84,13 @@ export default defineComponent({
directives: { pAspectRatio },
props : {
modelValue: {
type : String,
type : [String, globalThis.File],
default: '',
},
modelModifiers: {
type : Object as PropType<ModelModifier>,
default: () => ({} as ModelModifier),
},
width: {
type : Number,
default: 430,
Expand Down Expand Up @@ -118,6 +127,7 @@ export default defineComponent({
emits: ['update:modelValue'],
setup (props) {
const model = useVModel(props)
const preview = usePreview(model, '')
const rawModel = ref('')
const isOpen = ref(false)
const screen = useWindowSize()
Expand Down Expand Up @@ -145,9 +155,9 @@ export default defineComponent({
})
async function open () {
const result = model.value && mode.value === 'rotate'
? await rotateImage(model.value, 90)
: model.value
const result = preview.value && mode.value === 'rotate'
? await rotateImage(preview.value, 90)
: preview.value
rawModel.value = result
isOpen.value = true
Expand All @@ -158,7 +168,11 @@ export default defineComponent({
? await rotateImage(rawModel.value, -90)
: rawModel.value
model.value = result
const value = props.modelModifiers.base64
? result
: fromBase64(result)
model.value = value
isOpen.value = false
}
Expand All @@ -173,6 +187,7 @@ export default defineComponent({
classNames,
style,
rawModel,
preview,
model,
isOpen,
mode,
Expand Down
Loading

0 comments on commit 7a7ac5f

Please sign in to comment.