@bagelink/vue 1.15.429 → 1.15.433

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.15.429",
4
+ "version": "1.15.433",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -199,6 +199,13 @@ const bind = computed(() => {
199
199
  }
200
200
  return obj
201
201
  })
202
+ function handleKeydown(event: KeyboardEvent) {
203
+ const element = event.currentTarget as HTMLElement
204
+ if (element.tagName === 'BUTTON' || (element.tagName === 'A' && event.key === 'Enter')) return
205
+ if (props.disabled || event.repeat) return
206
+ event.preventDefault()
207
+ element.click()
208
+ }
202
209
  const slots: SetupContext['slots'] = useSlots()
203
210
  </script>
204
211
 
@@ -226,7 +233,7 @@ const slots: SetupContext['slots'] = useSlots()
226
233
  'gradient': computedVariant === 'gradient',
227
234
  'bgl_pill-frame': frame,
228
235
  relative: loading,
229
- }, computedPairClass]" :style="gradientStyle" :tabindex="disabled ? -1 : 0" @click.stop="handleClick" @keydown.enter="handleClick" @keydown.space="handleClick"
236
+ }, computedPairClass]" :style="gradientStyle" :tabindex="disabled ? -1 : 0" @click.stop="handleClick" @keydown.enter="handleKeydown" @keydown.space="handleKeydown"
230
237
  >
231
238
  <!-- Keep label/icons mounted while loading so the button width/height don't jump.
232
239
  Hide them hard (visibility + opacity) so the icon never shows through the spinner.
@@ -173,7 +173,7 @@ function getItemRef(i: number) {
173
173
  {{ helptext }}
174
174
  </p>
175
175
  <template v-if="!collapsible">
176
- <div v-for="(_, i) in items" :key="i" class="array-input-row grid align-items-center" :class="{ 'simpleArray': isSimple, 'pb-1': !isSimple }">
176
+ <div v-for="(_, i) in items" :key="i" class="array-input-row grid align-items-center" :class="{ 'simpleArray mb-025': isSimple, 'pb-1': !isSimple }">
177
177
  <slot :item="getItemRef(i)" :index="i" />
178
178
  <div v-if="allowDelete" class=" h-100p" :class="{ 'border-start ps-025 ms-075': !simple }">
179
179
  <Btn
@@ -16,6 +16,7 @@ interface NumberInputProps extends BagelInputShellProps {
16
16
  iconStart?: IconType
17
17
  icon?: IconType
18
18
  label?: string
19
+ hideLabel?: boolean
19
20
  defaultValue?: number | string
20
21
  placeholder?: string
21
22
  disabled?: boolean
@@ -39,6 +40,7 @@ const {
39
40
  iconStart,
40
41
  icon,
41
42
  label,
43
+ hideLabel = false,
42
44
  placeholder,
43
45
  disabled,
44
46
  required,
@@ -181,7 +183,7 @@ const hasValue = computed(() => formattedValue.value.length > 0)
181
183
  ]" :style="shellStyle"
182
184
  >
183
185
  <div>
184
- <label v-if="label || (underlined && placeholder)" class="block">
186
+ <label v-if="!hideLabel && (label || (underlined && placeholder))" class="block">
185
187
  <span class="label-text">{{ resolveI18n(label) || resolveI18n(placeholder) }}<span v-if="required"> *</span></span>
186
188
  </label>
187
189
  <div class="gap-025" :class="{ 'column display-flex': layout === 'vertical', 'flex': layout === 'horizontal' }">
@@ -195,7 +197,8 @@ const hasValue = computed(() => formattedValue.value.length > 0)
195
197
  :id v-model.trim="formattedValue" v-pattern.number type="text" :class="{
196
198
  'txt-center': center,
197
199
  'min0': layout,
198
- }" :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || resolveI18n(label))" :disabled :required inputmode="decimal"
200
+ }" :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || (hideLabel ? undefined : resolveI18n(label)))" :disabled :required inputmode="decimal"
201
+ :aria-label="hideLabel ? (resolveI18n(label) || resolveI18n(placeholder)) : undefined"
199
202
  autocomplete="off" :pattern="useGrouping ? '^-?\\d{1,3}(,\\d{3})*(\.\\d+)?$' : '^-?\\d*\.?\d*$'"
200
203
  autocorrect="off" spellcheck="false" @input="inputHandler" @keydown.up.prevent="increment"
201
204
  @keydown.down.prevent="decrement"
@@ -26,6 +26,7 @@ const props = withDefaults(
26
26
  defineProps<{
27
27
  id?: string
28
28
  label?: string
29
+ hideLabel?: boolean
29
30
  groupName?: string
30
31
  options: RadioOptionsSource<ContextObjType>
31
32
  deletable?: boolean
@@ -173,8 +174,8 @@ function handleChange() {
173
174
  </script>
174
175
 
175
176
  <template>
176
- <div :class="containerClasses" :style="containerStyle" role="radiogroup" :aria-labelledby="label ? `${name}-label` : undefined">
177
- <p v-if="label" :id="`${name}-label`" class="group-label">
177
+ <div :class="containerClasses" :style="containerStyle" role="radiogroup" :aria-labelledby="label && !hideLabel ? `${name}-label` : undefined" :aria-label="hideLabel ? resolveI18n(label) : undefined">
178
+ <p v-if="label && !hideLabel" :id="`${name}-label`" class="group-label">
178
179
  {{ resolveI18n(label) }} <span v-if="required">*</span>
179
180
  </p>
180
181
  <div ref="inlineWrapper" class="radio-group-wrap" :class="[wrapperClass, { 'radio-group-wrap--inline': inline }]"
@@ -299,7 +299,7 @@ onMounted(() => {
299
299
  >
300
300
  <template #trigger>
301
301
  <label>
302
- <span v-if="label || (underlined && placeholder)" class="label-text">{{ resolveI18n(label)
302
+ <span v-if="!hideLabel && (label || (underlined && placeholder))" class="label-text">{{ resolveI18n(label)
303
303
  || resolveI18n(placeholder) }}<span v-if="required"> *</span></span>
304
304
  <div class="flex gap-05">
305
305
  <TextInput
@@ -6,6 +6,7 @@ import SignaturePad from 'signature_pad'
6
6
  import { computed, onMounted, ref, watch } from 'vue'
7
7
  import type { BagelInputShellProps } from './bagelInputShell'
8
8
  import { useBagelInputShell } from './bagelInputShell'
9
+ import { getSignaturePadPixelRatio, resizeSignatureCanvas } from './signaturePadSizing'
9
10
 
10
11
  export interface WaterMark {
11
12
  text: string
@@ -94,10 +95,11 @@ function saveFile() {
94
95
  }
95
96
 
96
97
  function fromDataURL(url: string) {
98
+ const bounds = vCanvas.value?.getBoundingClientRect()
97
99
  return sig.value?.fromDataURL(url, {
98
- height: vCanvas.value?.height,
99
- width: vCanvas.value?.width,
100
- ratio: props.ratio,
100
+ height: bounds?.height,
101
+ width: bounds?.width,
102
+ ratio: getSignaturePadPixelRatio(),
101
103
  xOffset: 0,
102
104
  yOffset: 0,
103
105
  })
@@ -195,17 +197,37 @@ watch(fileData, (newFile) => {
195
197
  })
196
198
 
197
199
  function resizeCanvas() {
198
- let url
199
- if (!isEmpty()) { url = save() }
200
- const ratio = props.ratio ?? 1 / 2
201
- const { width } = vCanvas.value!.getBoundingClientRect()
202
- vCanvas.value!.width = props.width ? Number(props.width.replaceAll('px', '')) : width
203
- vCanvas.value!.height = props.height ? Number(props.height.replaceAll('px', '')) : width * ratio
204
- clear()
205
- if (!props.clearOnResize && url !== undefined) { void fromDataURL(url) }
200
+ const canvas = vCanvas.value
201
+ const signature = sig.value
202
+ if (!canvas || !signature) { return }
203
+
204
+ const wasEmpty = signature.isEmpty()
205
+ const pointData = wasEmpty ? [] : signature.toData()
206
+ const imageData = !wasEmpty && pointData.length === 0 ? save() : undefined
207
+ if (!resizeSignatureCanvas(canvas)) { return }
208
+
209
+ signature.clear()
210
+ if (props.clearOnResize) {
211
+ _isEmpty.value = true
212
+ sigData.value = undefined
213
+ } else if (pointData.length > 0) {
214
+ signature.fromData(pointData)
215
+ _isEmpty.value = false
216
+ } else if (imageData) {
217
+ void fromDataURL(imageData)
218
+ _isEmpty.value = false
219
+ }
206
220
  if (props.waterMark) { addWaterMark(props.waterMark) }
207
221
  }
208
222
 
223
+ const canvasStyle = computed(() => ({
224
+ ...(props.backgroundColor ? { background: props.backgroundColor } : {}),
225
+ width: props.width ?? '100%',
226
+ ...(props.height
227
+ ? { height: props.height }
228
+ : { aspectRatio: `1 / ${props.ratio ?? 1 / 2}`, height: 'auto' }),
229
+ }))
230
+
209
231
  function draw() {
210
232
  sig.value = new SignaturePad(vCanvas.value!, signatureOptions.value)
211
233
  resizeCanvas()
@@ -253,7 +275,7 @@ defineExpose({
253
275
  ref="vCanvas"
254
276
  class="canvas"
255
277
  :disabled
256
- :style="backgroundColor ? { background: backgroundColor } : {}"
278
+ :style="canvasStyle"
257
279
  />
258
280
  <input
259
281
  placeholder="required"
@@ -273,8 +295,7 @@ defineExpose({
273
295
  <style>
274
296
 
275
297
  .bgl_input .canvas {
276
- width: 100%;
277
- height: 100%;
298
+ display: block;
278
299
  }
279
300
 
280
301
  .bgl_input.signature-pad {
@@ -0,0 +1,30 @@
1
+ export const MAX_SIGNATURE_PAD_PIXEL_RATIO = 2
2
+
3
+ export interface SignatureCanvasSize {
4
+ width: number
5
+ height: number
6
+ pixelRatio: number
7
+ }
8
+
9
+ export function getSignaturePadPixelRatio(
10
+ devicePixelRatio = globalThis.devicePixelRatio ?? 1,
11
+ ): number {
12
+ if (!Number.isFinite(devicePixelRatio)) { return 1 }
13
+ return Math.min(Math.max(devicePixelRatio, 1), MAX_SIGNATURE_PAD_PIXEL_RATIO)
14
+ }
15
+
16
+ export function resizeSignatureCanvas(
17
+ canvas: HTMLCanvasElement,
18
+ pixelRatio = getSignaturePadPixelRatio(),
19
+ ): SignatureCanvasSize | undefined {
20
+ const { width, height } = canvas.getBoundingClientRect()
21
+ if (width <= 0 || height <= 0) { return undefined }
22
+
23
+ const backingWidth = Math.round(width * pixelRatio)
24
+ const backingHeight = Math.round(height * pixelRatio)
25
+ if (canvas.width !== backingWidth) { canvas.width = backingWidth }
26
+ if (canvas.height !== backingHeight) { canvas.height = backingHeight }
27
+
28
+ canvas.getContext('2d')?.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0)
29
+ return { width, height, pixelRatio }
30
+ }