@mptw/skylens-ui 1.4.80 → 1.4.82

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.
@@ -46,21 +46,7 @@ function safeSheetName(name: string) {
46
46
  return name.replace(/[\\/*?:[\]]/g, "").substring(0, 31);
47
47
  }
48
48
 
49
- function loadImage(url: string) {
50
- return new Promise<HTMLImageElement>((resolve) => {
51
- const img = new Image();
52
- img.onload = function () {
53
- resolve(img);
54
- };
55
- img.src = url;
56
- });
57
- }
58
-
59
- const DEFAULT_STYLE = {
60
- marginTop: 0,
61
- marginBottom: 9,
62
- border: 0,
63
- };
49
+ const WatermarkImage = '/assets/images/commons/watermark-2.svg';
64
50
 
65
51
  export default defineComponent({
66
52
  name: "SLDownloadButton",
@@ -137,22 +123,6 @@ export default defineComponent({
137
123
  let clickSubscriber: Subscription | null = null;
138
124
  let popperInstance: PopperInstance | null = null;
139
125
 
140
- const watermarkImg = computed(() => '/assets/images/commons/watermark-2.svg')
141
- const styleOption = computed(() => {
142
- return Object.assign({}, DEFAULT_STYLE, customStyle.value);
143
- });
144
- const filterOption = computed(() => {
145
- return (node: HTMLElement) => {
146
- return (
147
- !(
148
- node?.getAttribute && node.getAttribute("data-skip-download") === ""
149
- ) &&
150
- (typeof customFilter.value !== "function" ||
151
- (customFilter.value && customFilter.value(node)))
152
- );
153
- };
154
- });
155
-
156
126
  function subscribeClick() {
157
127
  clickSubscriber = fromEvent(window, "click").subscribe((e) => {
158
128
  const { target } = e;
@@ -252,10 +222,8 @@ export default defineComponent({
252
222
  return;
253
223
  }
254
224
 
255
- if (!imageTarget.value) {
256
- console.log("Can not find target of htmlToImage!");
257
- return;
258
- }
225
+ const dataURL = await domToPngDataUrl(imageTarget.value, customFilter.value, customStyle.value, padding.value, dateRanges.value, watermark.value, WatermarkImage);
226
+ downloadURL(dataURL, `${fileName.value}.png`);
259
227
 
260
228
  if (evCategory.value !== "") {
261
229
  gaev({
@@ -269,93 +237,6 @@ export default defineComponent({
269
237
  label: "下載圖片",
270
238
  });
271
239
  }
272
-
273
- const options = {
274
- pixelRatio: 1,
275
- filter: filterOption.value,
276
- style: styleOption.value,
277
- };
278
-
279
- let dataURL = await htmlToImage.toPng(imageTarget.value, options);
280
- if (padding.value) dataURL = await addPadding(dataURL, padding.value);
281
- if (dateRanges.value?.length > 0)
282
- dataURL = await addDateRanges(dataURL, [...dateRanges.value].reverse());
283
- if (watermark.value) dataURL = await addWatermark(dataURL, watermarkImg.value);
284
- downloadURL(dataURL, `${fileName.value}.png`);
285
- }
286
-
287
- async function addPadding(dataURL: string, padding: number) {
288
- const canvas = document.createElement("canvas");
289
- const ctx = canvas.getContext("2d");
290
- if (!ctx) return "";
291
-
292
- const img = await loadImage(dataURL);
293
- const { width, height } = img;
294
- canvas.width = width + padding * 2;
295
- canvas.height = height + padding * 2;
296
- ctx.fillStyle = "#FFFFFF";
297
- ctx.fillRect(0, 0, canvas.width, canvas.height);
298
- ctx.drawImage(img, padding, padding);
299
-
300
- return canvas.toDataURL();
301
- }
302
-
303
- async function addDateRanges(
304
- dataURL: string,
305
- dateRanges: (IDateRange | null)[]
306
- ) {
307
- const fontSize = 16;
308
- const canvas = document.createElement("canvas");
309
- const ctx = canvas.getContext("2d");
310
- if (!ctx) return "";
311
-
312
- const img = await loadImage(dataURL);
313
- const { width, height } = img;
314
- canvas.width = width;
315
- canvas.height = height + fontSize * 3;
316
- ctx.fillStyle = "#FFFFFF";
317
- ctx.fillRect(0, 0, canvas.width, canvas.height);
318
- ctx.drawImage(img, 0, 0);
319
-
320
- const text = `時間區間:${dateRanges
321
- .filter((dateRange) => !!dateRange)
322
- .map((dateRange) => formatDateRange(dateRange))
323
- .join(" v.s. ")}`;
324
- ctx.fillStyle = "#333333";
325
- ctx.font = `${fontSize}px "Microsoft JhengHei"`;
326
- ctx.fillText(text, fontSize, canvas.height - fontSize);
327
-
328
- return canvas.toDataURL();
329
- }
330
-
331
- async function addWatermark(dataURL: string, watermarkImg: any) {
332
- const canvas = document.createElement("canvas");
333
- const ctx = canvas.getContext("2d");
334
- if (!ctx) return "";
335
-
336
- const img = await loadImage(dataURL);
337
- const watermark = await loadImage(watermarkImg);
338
-
339
- const { width, height } = img;
340
- canvas.width = width;
341
- canvas.height = height;
342
-
343
- ctx.fillStyle = "#FFFFFF";
344
- ctx.fillRect(0, 0, canvas.width, canvas.height);
345
- ctx.drawImage(img, 0, 0);
346
-
347
- // const matrix = new DOMMatrix();
348
- // const pattern = ctx.createPattern(watermark, "repeat");
349
- // if (!pattern) return "";
350
-
351
- // pattern.setTransform(matrix.rotate(-20).scale(0.5));
352
- // ctx.globalAlpha = 0.05;
353
- // ctx.fillStyle = pattern;
354
- // ctx.fillRect(0, 0, canvas.width, canvas.height);
355
-
356
- ctx.drawImage(watermark, canvas.width - watermark.width - 12, canvas.height - watermark.height - 12);
357
-
358
- return canvas.toDataURL();
359
240
  }
360
241
 
361
242
  onMounted(() => {
@@ -4,12 +4,12 @@
4
4
  ref="el"
5
5
  )
6
6
  input(
7
- type="checkbox"
8
- :id="uid"
9
- v-model="modelProxy"
10
- :true-value="trueValue",
11
- :false-value="falseValue",
12
- )
7
+ type="checkbox"
8
+ :id="uid"
9
+ v-model="modelProxy"
10
+ :true-value="trueValue",
11
+ :false-value="falseValue",
12
+ )
13
13
  label(:for="uid" :style="`width: ${labelWidth}px;`" ref="labelEl")
14
14
  span(ref="trueLabelEl").true-label {{ size === '' ? trueLabel : minTrueLabel }}
15
15
  span(ref="falseLabelEl").false-label {{ size === '' ? falseLabel : minFalseLabel }}
@@ -52,7 +52,7 @@ export default defineComponent({
52
52
  size: {
53
53
  type: String,
54
54
  default: "",
55
- validator: (val: string) => ["", "min", "sm"].includes(val),
55
+ validator: (val: string) => ["", "min", "sm", "xs"].includes(val),
56
56
  },
57
57
  color: {
58
58
  type: String,
@@ -198,6 +198,19 @@ export default defineComponent({
198
198
  .false-label
199
199
  @apply s('pl-[31px]')
200
200
 
201
+ &.ios-switch-xs
202
+ label
203
+ @apply s('min-w-[60px]') s('h-[24px]') text-xs
204
+
205
+ &:before
206
+ @apply s('inset-y-0.75') s('left-0.75') s('w-[16px]') border border-primary-100 rounded-full shadow-none
207
+
208
+ .true-label
209
+ @apply s('pr-[27px]')
210
+
211
+ .false-label
212
+ @apply s('pl-[27px]')
213
+
201
214
  &.ios-switch-primary-600
202
215
  input:checked + label
203
216
  @apply bg-primary-600 border-primary
@@ -35,14 +35,14 @@
35
35
  v-else
36
36
  )
37
37
  .sidebar-mode-setting
38
- .sidebar-mode-setting-title 模式
38
+ .sidebar-mode-setting-title 選單模式
39
39
  SLIOSSwitch(
40
40
  :modelValue="dark"
41
41
  trueLabel="深色"
42
42
  minTrueLabel="深"
43
43
  falseLabel="淺色"
44
44
  minFalseLabel="淺"
45
- :size="closuredProxy ? 'min' : ''"
45
+ :size="closuredProxy ? 'min' : 'xs'"
46
46
  :color='switchColor'
47
47
  @update:modelValue="onUpdatedMode"
48
48
  )
@@ -51,7 +51,7 @@
51
51
  SLIcon(icon="chevron-right")
52
52
  .sidebar-nav-intro(ref='introEl')
53
53
  .sidebar-nav-intro-header {{ navLinkIntro ? navLinkIntro.title : '' }}
54
- .sidebar-nav-intro-body {{ navLinkIntro ? navLinkIntro.content : ''}}
54
+ .sidebar-nav-intro-body {{ navLinkIntro ? navLinkIntro.content : '' }}
55
55
  .sidebar-nav-intro-footer
56
56
  SLButton(
57
57
  title='我知道了',
@@ -81,7 +81,7 @@ export default defineComponent({
81
81
  },
82
82
  config: {
83
83
  type: Array as PropType<NavConfigType>,
84
- default: () => [],
84
+ default: () => [],
85
85
  },
86
86
  navIntros: {
87
87
  type: Array as PropType<{
@@ -116,7 +116,7 @@ export default defineComponent({
116
116
 
117
117
  function onClickedToggle() {
118
118
  if (introing.value) return;
119
-
119
+
120
120
  closuredProxy.value = !closuredProxy.value;
121
121
  }
122
122
 
@@ -287,7 +287,7 @@ export default defineComponent({
287
287
  @apply bg-nav-color-switch-bg border-primary-600
288
288
 
289
289
  &:before
290
- @apply border-2 border-primary-600 bg-primary-300
290
+ @apply border-primary-600 bg-primary-300
291
291
 
292
292
  .sidebar-toggle
293
293
  @apply bg-primary-900 text-white
@@ -373,19 +373,19 @@ export default defineComponent({
373
373
  @apply w-full h-px my-2 bg-primary-200
374
374
 
375
375
  .sidebar-mode-setting
376
- @apply flex-grow-0 flex-shrink-0 flex items-center justify-end my-4 pr-4 s('pl-[42px]') space-x-2 text-base text-primary
376
+ @apply flex-grow-0 flex-shrink-0 flex items-center justify-start my-4 pl-4 space-x-2 text-xs text-primary
377
377
 
378
378
  &-title
379
379
  @apply flex-grow-0 flex-shrink-0
380
380
 
381
381
  .ios-switch
382
- @apply flex-grow
382
+ @apply flex-grow-0 flex-shrink-0
383
383
 
384
384
  label
385
385
  @apply border-primary-100 bg-primary-25
386
386
 
387
387
  &:before
388
- @apply border-2 border-primary-100
388
+ @apply border-primary-100
389
389
 
390
390
  &-toggle
391
391
  @apply absolute top-0 right-0 flex justify-center items-center w-5 h-10 s('mt-2.5') bg-primary-50 rounded-l-xl text-sm text-primary-500 leading-none transition-all
@@ -424,5 +424,4 @@ export default defineComponent({
424
424
  &:before
425
425
  content ''
426
426
  @apply visible
427
- </style>
428
-
427
+ </style>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mptw/skylens-ui",
3
3
  "type": "module",
4
- "version": "1.4.80",
4
+ "version": "1.4.82",
5
5
  "main": "./nuxt.config.ts",
6
6
  "scripts": {
7
7
  "dev": "nuxi dev .playground",
package/utils/index.ts CHANGED
@@ -21,6 +21,7 @@ import type IDateRange from '~ui/interface/IDateRange';
21
21
  import { ScreenSize } from '~ui/interface/commons';
22
22
  import ExcelJS from 'exceljs';
23
23
  import { useGtm } from '@gtm-support/vue-gtm';
24
+ import * as htmlToImage from "html-to-image";
24
25
 
25
26
  export const getUID = () => {
26
27
  return `uid-${Math.random().toString(36).substring(2)}`;
@@ -574,4 +575,131 @@ export const waitFor = async (condition: () => boolean, pollInterval = 50, timeo
574
575
 
575
576
  await new Promise(r => setTimeout(r, pollInterval));
576
577
  }
578
+ }
579
+
580
+ function loadImage(url: string) {
581
+ return new Promise<HTMLImageElement>((resolve) => {
582
+ const img = new Image();
583
+ img.onload = function () {
584
+ resolve(img);
585
+ };
586
+ img.src = url;
587
+ });
588
+ }
589
+
590
+ async function addPadding(dataURL: string, padding: number) {
591
+ const canvas = document.createElement("canvas");
592
+ const ctx = canvas.getContext("2d");
593
+ if (!ctx) return "";
594
+
595
+ const img = await loadImage(dataURL);
596
+ const { width, height } = img;
597
+ canvas.width = width + padding * 2;
598
+ canvas.height = height + padding * 2;
599
+ ctx.fillStyle = "#FFFFFF";
600
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
601
+ ctx.drawImage(img, padding, padding);
602
+
603
+ return canvas.toDataURL();
604
+ }
605
+
606
+ async function addDateRanges(
607
+ dataURL: string,
608
+ dateRanges: (IDateRange | null)[]
609
+ ) {
610
+ const fontSize = 16;
611
+ const canvas = document.createElement("canvas");
612
+ const ctx = canvas.getContext("2d");
613
+ if (!ctx) return "";
614
+
615
+ const img = await loadImage(dataURL);
616
+ const { width, height } = img;
617
+ canvas.width = width;
618
+ canvas.height = height + fontSize * 3;
619
+ ctx.fillStyle = "#FFFFFF";
620
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
621
+ ctx.drawImage(img, 0, 0);
622
+
623
+ const text = `時間區間:${dateRanges
624
+ .filter((dateRange) => !!dateRange)
625
+ .map((dateRange) => formatDateRange(dateRange))
626
+ .join(" v.s. ")}`;
627
+ ctx.fillStyle = "#333333";
628
+ ctx.font = `${fontSize}px "Microsoft JhengHei"`;
629
+ ctx.fillText(text, fontSize, canvas.height - fontSize);
630
+
631
+ return canvas.toDataURL();
632
+ }
633
+
634
+ async function addWatermark(dataURL: string, watermarkImg: string) {
635
+ const canvas = document.createElement("canvas");
636
+ const ctx = canvas.getContext("2d");
637
+ if (!ctx) return "";
638
+
639
+ const img = await loadImage(dataURL);
640
+ const watermark = await loadImage(watermarkImg);
641
+
642
+ const { width, height } = img;
643
+ canvas.width = width;
644
+ canvas.height = height;
645
+
646
+ ctx.fillStyle = "#FFFFFF";
647
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
648
+ ctx.drawImage(img, 0, 0);
649
+
650
+ // const matrix = new DOMMatrix();
651
+ // const pattern = ctx.createPattern(watermark, "repeat");
652
+ // if (!pattern) return "";
653
+
654
+ // pattern.setTransform(matrix.rotate(-20).scale(0.5));
655
+ // ctx.globalAlpha = 0.05;
656
+ // ctx.fillStyle = pattern;
657
+ // ctx.fillRect(0, 0, canvas.width, canvas.height);
658
+
659
+ ctx.drawImage(watermark, canvas.width - watermark.width - 12, canvas.height - watermark.height - 12);
660
+
661
+ return canvas.toDataURL();
662
+ }
663
+
664
+ export const domToPngDataUrl = async (
665
+ imageTarget: HTMLElement | null,
666
+ customFilter: Function | null,
667
+ customStyle: CSSStyleDeclaration | null,
668
+ padding: number = 0,
669
+ dateRanges: (IDateRange | null)[] = [],
670
+ watermark: boolean = true,
671
+ watermarkImg: string = '/assets/images/commons/watermark-2.svg',
672
+ ) => {
673
+ if (!imageTarget) {
674
+ console.log("Can not find target of htmlToImage!");
675
+ return;
676
+ }
677
+
678
+ const filterOption = (node: HTMLElement) => {
679
+ return (
680
+ !(
681
+ node?.getAttribute && node.getAttribute("data-skip-download") === ""
682
+ ) &&
683
+ (typeof customFilter !== "function" ||
684
+ (customFilter && customFilter(node)))
685
+ );
686
+ };
687
+ const styleOption = Object.assign({}, {
688
+ marginTop: '0px',
689
+ marginBottom: '9px',
690
+ border: 'none',
691
+ }, customStyle || {});
692
+
693
+ const options = {
694
+ pixelRatio: 1,
695
+ filter: filterOption,
696
+ style: styleOption,
697
+ };
698
+
699
+ let dataURL = await htmlToImage.toPng(imageTarget, options);
700
+ if (padding) dataURL = await addPadding(dataURL, padding);
701
+ if (dateRanges?.length)
702
+ dataURL = await addDateRanges(dataURL, [...dateRanges].reverse());
703
+ if (watermark) dataURL = await addWatermark(dataURL, watermarkImg);
704
+ return dataURL;
577
705
  }