| @@ -1,188 +1,200 @@ |
| 1 | | -import type { BarcodeOptions } from '@stacksjs/qrx' |
| 2 | | -import type { Component, PropType } from 'vue' |
| 3 | | -import { barcode } from '@stacksjs/qrx' |
| 4 | | -import { defineComponent, h } from 'vue' |
| 1 | +import type { BarcodeOptions, QRCodeOptions } from '@stacksjs/qrx' |
| 2 | +import { barcode, QRCode, QRErrorCorrectLevel } from '@stacksjs/qrx' |
| 3 | +import { defineComponent, h, onMounted, ref, watch } from 'vue' |
| 5 | 4 | |
| 6 | | -interface BarcodeData { |
| 7 | | - valid: boolean |
| 8 | | -} |
| 9 | | - |
| 10 | | -export const VueBarcode: Component = defineComponent({ |
| 5 | +export const VueBarcode = defineComponent({ |
| 11 | 6 | name: 'VueBarcode', |
| 12 | 7 | |
| 13 | | - render() { |
| 14 | | - return h('div', [ |
| 15 | | - h(this.elementTag, { |
| 16 | | - style: { display: this.valid ? undefined : 'none' }, |
| 17 | | - class: ['vue-barcode-element'], |
| 18 | | - }), |
| 19 | | - |
| 20 | | - h('div', { |
| 21 | | - style: { display: this.valid ? 'none' : undefined }, |
| 22 | | - }, this.$slots.default?.()), |
| 23 | | - ]) |
| 24 | | - }, |
| 25 | | - |
| 26 | 8 | props: { |
| 27 | 9 | value: { |
| 28 | | - type: [String, Number] as PropType<string | number>, |
| 10 | + type: [String, Number], |
| 29 | 11 | required: true, |
| 30 | 12 | }, |
| 31 | | - |
| 32 | | - format: { |
| 33 | | - type: String as PropType<string>, |
| 34 | | - }, |
| 35 | | - |
| 36 | | - width: { |
| 37 | | - type: [String, Number] as PropType<string | number>, |
| 38 | | - }, |
| 39 | | - |
| 40 | | - height: { |
| 41 | | - type: [String, Number] as PropType<string | number>, |
| 42 | | - }, |
| 43 | | - |
| 13 | + format: String, |
| 14 | + width: [String, Number], |
| 15 | + height: [String, Number], |
| 44 | 16 | displayValue: { |
| 45 | | - type: [String, Boolean] as PropType<string | boolean>, |
| 17 | + type: [String, Boolean], |
| 46 | 18 | default: true, |
| 47 | 19 | }, |
| 48 | | - |
| 49 | | - text: { |
| 50 | | - type: [String, Number] as PropType<string | number>, |
| 51 | | - }, |
| 52 | | - |
| 53 | | - fontOptions: { |
| 54 | | - type: String as PropType<string>, |
| 55 | | - }, |
| 56 | | - |
| 57 | | - font: { |
| 58 | | - type: String as PropType<string>, |
| 59 | | - }, |
| 60 | | - |
| 61 | | - textAlign: { |
| 62 | | - type: String as PropType<string>, |
| 63 | | - }, |
| 64 | | - |
| 65 | | - textPosition: { |
| 66 | | - type: String as PropType<string>, |
| 67 | | - }, |
| 68 | | - |
| 69 | | - textMargin: { |
| 70 | | - type: [String, Number] as PropType<string | number>, |
| 20 | + text: [String, Number], |
| 21 | + fontOptions: String, |
| 22 | + font: String, |
| 23 | + textAlign: String, |
| 24 | + textPosition: String, |
| 25 | + textMargin: [String, Number], |
| 26 | + fontSize: [String, Number], |
| 27 | + background: String, |
| 28 | + lineColor: String, |
| 29 | + margin: [String, Number], |
| 30 | + marginTop: [String, Number], |
| 31 | + marginBottom: [String, Number], |
| 32 | + marginLeft: [String, Number], |
| 33 | + marginRight: [String, Number], |
| 34 | + flat: Boolean, |
| 35 | + ean128: [String, Boolean], |
| 36 | + elementTag: { |
| 37 | + type: String, |
| 38 | + default: 'svg', |
| 39 | + validator: (value: string) => ['canvas', 'svg', 'img'].includes(value), |
| 71 | 40 | }, |
| 41 | + }, |
| 72 | 42 | |
| 73 | | - fontSize: { |
| 74 | | - type: [String, Number] as PropType<string | number>, |
| 75 | | - }, |
| 43 | + setup(props, { slots }) { |
| 44 | + const valid = ref(true) |
| 45 | + const elementRef = ref<HTMLElement | null>(null) |
| 76 | 46 | |
| 77 | | - background: { |
| 78 | | - type: String as PropType<string>, |
| 79 | | - }, |
| 47 | + function renderBarcode() { |
| 48 | + const el = elementRef.value |
| 49 | + if (!el) |
| 50 | + return |
| 80 | 51 | |
| 81 | | - lineColor: { |
| 82 | | - type: String as PropType<string>, |
| 83 | | - }, |
| 52 | + const settings: BarcodeOptions = { |
| 53 | + format: props.format, |
| 54 | + width: props.width, |
| 55 | + height: props.height, |
| 56 | + displayValue: props.displayValue, |
| 57 | + text: props.text, |
| 58 | + fontOptions: props.fontOptions, |
| 59 | + font: props.font, |
| 60 | + textAlign: props.textAlign, |
| 61 | + textPosition: props.textPosition, |
| 62 | + textMargin: props.textMargin, |
| 63 | + fontSize: props.fontSize, |
| 64 | + background: props.background, |
| 65 | + lineColor: props.lineColor, |
| 66 | + margin: props.margin, |
| 67 | + marginTop: props.marginTop, |
| 68 | + marginBottom: props.marginBottom, |
| 69 | + marginLeft: props.marginLeft, |
| 70 | + marginRight: props.marginRight, |
| 71 | + flat: props.flat, |
| 72 | + ean128: props.ean128, |
| 73 | + elementTag: props.elementTag, |
| 74 | + valid: (v: boolean) => { |
| 75 | + valid.value = v |
| 76 | + }, |
| 77 | + } |
| 84 | 78 | |
| 85 | | - margin: { |
| 86 | | - type: [String, Number] as PropType<string | number>, |
| 87 | | - }, |
| 79 | + |
| 80 | + for (const key of Object.keys(settings) as Array<keyof BarcodeOptions>) { |
| 81 | + if (settings[key] === undefined) |
| 82 | + delete settings[key] |
| 83 | + } |
| 88 | 84 | |
| 89 | | - marginTop: { |
| 90 | | - type: [String, Number] as PropType<string | number>, |
| 91 | | - }, |
| 85 | + try { |
| 86 | + barcode(el, String(props.value), settings) |
| 87 | + } |
| 88 | + catch { |
| 89 | + valid.value = false |
| 90 | + } |
| 91 | + } |
| 92 | 92 | |
| 93 | | - marginBottom: { |
| 94 | | - type: [String, Number] as PropType<string | number>, |
| 95 | | - }, |
| 93 | + onMounted(renderBarcode) |
| 96 | 94 | |
| 97 | | - marginLeft: { |
| 98 | | - type: [String, Number] as PropType<string | number>, |
| 99 | | - }, |
| 95 | + watch(() => ({ ...props }), renderBarcode, { deep: true }) |
| 100 | 96 | |
| 101 | | - marginRight: { |
| 102 | | - type: [String, Number] as PropType<string | number>, |
| 103 | | - }, |
| 97 | + return () => h('div', [ |
| 98 | + h(props.elementTag, { |
| 99 | + ref: (el: any) => { elementRef.value = el }, |
| 100 | + style: { display: valid.value ? undefined : 'none' }, |
| 101 | + class: 'vue-barcode-element', |
| 102 | + }), |
| 103 | + h('div', { |
| 104 | + style: { display: valid.value ? 'none' : undefined }, |
| 105 | + }, slots.default?.()), |
| 106 | + ]) |
| 107 | + }, |
| 108 | +}) |
| 104 | 109 | |
| 105 | | - flat: { |
| 106 | | - type: Boolean as PropType<boolean>, |
| 107 | | - }, |
| 110 | +export const VueQRCode = defineComponent({ |
| 111 | + name: 'VueQRCode', |
| 108 | 112 | |
| 109 | | - ean128: { |
| 110 | | - type: [String, Boolean] as PropType<string | boolean>, |
| 113 | + props: { |
| 114 | + value: { |
| 115 | + type: String, |
| 116 | + required: true, |
| 111 | 117 | }, |
| 112 | | - |
| 113 | | - elementTag: { |
| 114 | | - type: String as PropType<string>, |
| 115 | | - default: 'svg', |
| 116 | | - validator(value: string): boolean { |
| 117 | | - return ['canvas', 'svg', 'img'].includes(value) |
| 118 | | - }, |
| 118 | + width: { |
| 119 | + type: Number, |
| 120 | + default: 256, |
| 119 | 121 | }, |
| 120 | | - }, |
| 121 | | - |
| 122 | | - data(): BarcodeData { |
| 123 | | - return { valid: true } |
| 124 | | - }, |
| 125 | | - |
| 126 | | - watch: { |
| 127 | | - $props: { |
| 128 | | - handler() { |
| 129 | | - this.renderBarcode() |
| 130 | | - }, |
| 131 | | - deep: true, |
| 132 | | - immediate: true, |
| 122 | + height: { |
| 123 | + type: Number, |
| 124 | + default: 256, |
| 125 | + }, |
| 126 | + colorDark: { |
| 127 | + type: String, |
| 128 | + default: '#000000', |
| 129 | + }, |
| 130 | + colorLight: { |
| 131 | + type: String, |
| 132 | + default: '#ffffff', |
| 133 | + }, |
| 134 | + correctLevel: { |
| 135 | + type: Number, |
| 136 | + default: QRErrorCorrectLevel.M, |
| 137 | + validator: (value: number) => [ |
| 138 | + QRErrorCorrectLevel.L, |
| 139 | + QRErrorCorrectLevel.M, |
| 140 | + QRErrorCorrectLevel.Q, |
| 141 | + QRErrorCorrectLevel.H, |
| 142 | + ].includes(value), |
| 143 | + }, |
| 144 | + useSVG: { |
| 145 | + type: Boolean, |
| 146 | + default: false, |
| 133 | 147 | }, |
| 134 | 148 | }, |
| 135 | 149 | |
| 136 | | - mounted() { |
| 137 | | - this.renderBarcode() |
| 138 | | - }, |
| 150 | + setup(props, { slots }) { |
| 151 | + const containerRef = ref<HTMLElement | null>(null) |
| 152 | + const valid = ref(true) |
| 153 | + let qrInstance: QRCode | null = null |
| 154 | + |
| 155 | + function renderQRCode() { |
| 156 | + const el = containerRef.value |
| 157 | + if (!el) |
| 158 | + return |
| 159 | + |
| 160 | + |
| 161 | + el.innerHTML = '' |
| 162 | + |
| 163 | + const options: QRCodeOptions = { |
| 164 | + text: props.value, |
| 165 | + width: props.width, |
| 166 | + height: props.height, |
| 167 | + colorDark: props.colorDark, |
| 168 | + colorLight: props.colorLight, |
| 169 | + correctLevel: props.correctLevel, |
| 170 | + useSVG: props.useSVG, |
| 171 | + } |
| 139 | 172 | |
| 140 | | - methods: { |
| 141 | | - renderBarcode() { |
| 142 | | - const settings: BarcodeOptions = { |
| 143 | | - format: this.format, |
| 144 | | - width: this.width, |
| 145 | | - height: this.height, |
| 146 | | - displayValue: this.displayValue, |
| 147 | | - text: this.text, |
| 148 | | - fontOptions: this.fontOptions, |
| 149 | | - font: this.font, |
| 150 | | - textAlign: this.textAlign, |
| 151 | | - textPosition: this.textPosition, |
| 152 | | - textMargin: this.textMargin, |
| 153 | | - fontSize: this.fontSize, |
| 154 | | - background: this.background, |
| 155 | | - lineColor: this.lineColor, |
| 156 | | - margin: this.margin, |
| 157 | | - marginTop: this.marginTop, |
| 158 | | - marginBottom: this.marginBottom, |
| 159 | | - marginLeft: this.marginLeft, |
| 160 | | - marginRight: this.marginRight, |
| 161 | | - flat: this.flat, |
| 162 | | - ean128: this.ean128, |
| 163 | | - valid: (valid: boolean) => { |
| 164 | | - this.valid = valid |
| 165 | | - }, |
| 166 | | - elementTag: this.elementTag, |
| 173 | + try { |
| 174 | + qrInstance = new QRCode(el, options) |
| 175 | + valid.value = true |
| 176 | + } |
| 177 | + catch { |
| 178 | + valid.value = false |
| 179 | + qrInstance = null |
| 167 | 180 | } |
| 181 | + } |
| 168 | 182 | |
| 169 | | - this.removeUndefinedProps(settings) |
| 183 | + onMounted(renderQRCode) |
| 170 | 184 | |
| 171 | | - const element = this.$el.querySelector('.vue-barcode-element') |
| 172 | | - if (element) { |
| 173 | | - barcode(element, String(this.value), settings) |
| 174 | | - } |
| 175 | | - }, |
| 185 | + watch(() => ({ ...props }), renderQRCode, { deep: true }) |
| 176 | 186 | |
| 177 | | - removeUndefinedProps(obj: BarcodeOptions): void { |
| 178 | | - for (const prop in obj) { |
| 179 | | - if (Object.prototype.hasOwnProperty.call(obj, prop)) { |
| 180 | | - const value = obj[prop as keyof BarcodeOptions] |
| 181 | | - if (value === undefined) { |
| 182 | | - delete obj[prop as keyof BarcodeOptions] |
| 183 | | - } |
| 184 | | - } |
| 185 | | - } |
| 186 | | - }, |
| 187 | + return () => h('div', [ |
| 188 | + h('div', { |
| 189 | + ref: (el: any) => { containerRef.value = el }, |
| 190 | + class: 'vue-qrcode-element', |
| 191 | + style: { display: valid.value ? undefined : 'none' }, |
| 192 | + }), |
| 193 | + h('div', { |
| 194 | + style: { display: valid.value ? 'none' : undefined }, |
| 195 | + }, slots.default?.()), |
| 196 | + ]) |
| 187 | 197 | }, |
| 188 | 198 | }) |
| 199 | + |
| 200 | +export { QRErrorCorrectLevel } from '@stacksjs/qrx' |