@14ch/svelte-ui 0.0.8 → 0.0.9
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.
|
@@ -366,12 +366,23 @@
|
|
|
366
366
|
}
|
|
367
367
|
};
|
|
368
368
|
|
|
369
|
+
const getNormalizedRange = () => {
|
|
370
|
+
if (mode !== 'range' || !value || !('start' in value && 'end' in value)) return null;
|
|
371
|
+
|
|
372
|
+
const startDate = dayjs(value.start).startOf('day');
|
|
373
|
+
const endDate = dayjs(value.end).startOf('day');
|
|
374
|
+
|
|
375
|
+
if (startDate.isSameOrBefore(endDate)) {
|
|
376
|
+
return { start: startDate, end: endDate };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return { start: endDate, end: startDate };
|
|
380
|
+
};
|
|
381
|
+
|
|
369
382
|
const isSelected = (date: dayjs.Dayjs) => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
dayjs(date).isSameOrBefore(dayjs(value.end).startOf('day'))
|
|
374
|
-
);
|
|
383
|
+
const range = getNormalizedRange();
|
|
384
|
+
if (range) {
|
|
385
|
+
return dayjs(date).isSameOrAfter(range.start) && dayjs(date).isSameOrBefore(range.end);
|
|
375
386
|
} else if (mode === 'single' && value && value instanceof Date) {
|
|
376
387
|
return dayjs(date).isSame(dayjs(value).startOf('day'));
|
|
377
388
|
}
|
|
@@ -379,25 +390,29 @@
|
|
|
379
390
|
};
|
|
380
391
|
|
|
381
392
|
const isRangeStart = (date: dayjs.Dayjs) => {
|
|
382
|
-
|
|
393
|
+
const range = getNormalizedRange();
|
|
394
|
+
if (!range) return false;
|
|
383
395
|
if (isRangePreviewActive) return false;
|
|
384
|
-
return dayjs(date).isSame(
|
|
396
|
+
return dayjs(date).isSame(range.start);
|
|
385
397
|
};
|
|
386
398
|
|
|
387
399
|
const isRangeEnd = (date: dayjs.Dayjs) => {
|
|
388
|
-
|
|
400
|
+
const range = getNormalizedRange();
|
|
401
|
+
if (!range) return false;
|
|
389
402
|
if (isRangePreviewActive) return false;
|
|
390
|
-
return dayjs(date).isSame(
|
|
403
|
+
return dayjs(date).isSame(range.end);
|
|
391
404
|
};
|
|
392
405
|
|
|
393
406
|
const isRangeMiddle = (date: dayjs.Dayjs) => {
|
|
394
|
-
|
|
407
|
+
const range = getNormalizedRange();
|
|
408
|
+
if (!range) return false;
|
|
395
409
|
if (isRangePreviewActive) return false;
|
|
396
410
|
return isSelected(date) && !isRangeStart(date) && !isRangeEnd(date);
|
|
397
411
|
};
|
|
398
412
|
|
|
399
413
|
const isRangeSingle = (date: dayjs.Dayjs) => {
|
|
400
|
-
|
|
414
|
+
const range = getNormalizedRange();
|
|
415
|
+
if (!range) return false;
|
|
401
416
|
if (isRangePreviewActive) return false;
|
|
402
417
|
return isRangeStart(date) && isRangeEnd(date);
|
|
403
418
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
8
8
|
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
9
9
|
import { t } from '../i18n';
|
|
10
|
+
import { convertToHtmlWithLink } from '../utils/formatText';
|
|
10
11
|
|
|
11
12
|
// =========================================================================
|
|
12
13
|
// Props, States & Constants
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
required = false,
|
|
59
60
|
clearable = false,
|
|
60
61
|
clearButtonAriaLabel = t('input.clear'),
|
|
62
|
+
linkify = false,
|
|
61
63
|
|
|
62
64
|
// フォーカスイベント
|
|
63
65
|
onfocus = () => {}, // No params for type inference
|
|
@@ -148,6 +150,7 @@
|
|
|
148
150
|
readonly?: boolean;
|
|
149
151
|
required?: boolean;
|
|
150
152
|
clearable?: boolean;
|
|
153
|
+
linkify?: boolean;
|
|
151
154
|
|
|
152
155
|
// フォーカスイベント
|
|
153
156
|
onfocus?: Function; // No params for type inference
|
|
@@ -374,6 +377,12 @@
|
|
|
374
377
|
return String(value);
|
|
375
378
|
};
|
|
376
379
|
|
|
380
|
+
const linkHtmlValue = $derived.by(() => {
|
|
381
|
+
if (!linkify) return '';
|
|
382
|
+
const result = convertToHtmlWithLink(getDisplayValue());
|
|
383
|
+
return typeof result === 'string' ? result : String(result ?? '');
|
|
384
|
+
});
|
|
385
|
+
|
|
377
386
|
const widthStyle = $derived(getStyleFromNumber(width));
|
|
378
387
|
const maxWidthStyle = $derived(getStyleFromNumber(maxWidth));
|
|
379
388
|
const minWidthStyle = $derived(getStyleFromNumber(minWidth));
|
|
@@ -384,6 +393,7 @@
|
|
|
384
393
|
input--focus-{focusStyle}
|
|
385
394
|
input--type-{type}"
|
|
386
395
|
class:input--inline={inline}
|
|
396
|
+
class:input--linkify={linkify}
|
|
387
397
|
class:input--auto-resize={inline}
|
|
388
398
|
class:input--full-width={fullWidth}
|
|
389
399
|
class:input--clearable={clearable}
|
|
@@ -456,6 +466,11 @@
|
|
|
456
466
|
{...restProps}
|
|
457
467
|
/>
|
|
458
468
|
</div>
|
|
469
|
+
{#if linkify}
|
|
470
|
+
<div class="input__link-text" style={customStyle}>
|
|
471
|
+
{@html linkHtmlValue}
|
|
472
|
+
</div>
|
|
473
|
+
{/if}
|
|
459
474
|
<!-- クリアボタン -->
|
|
460
475
|
{#if clearable && !disabled && !readonly}
|
|
461
476
|
<div class="input__clear-button">
|
|
@@ -613,6 +628,35 @@
|
|
|
613
628
|
}
|
|
614
629
|
}
|
|
615
630
|
|
|
631
|
+
/* リンク表示用オーバーレイ */
|
|
632
|
+
.input__link-text {
|
|
633
|
+
position: absolute;
|
|
634
|
+
top: 0;
|
|
635
|
+
left: 0;
|
|
636
|
+
width: 100%;
|
|
637
|
+
height: 100%;
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
padding: inherit;
|
|
641
|
+
background: transparent;
|
|
642
|
+
border-radius: inherit;
|
|
643
|
+
font-size: inherit;
|
|
644
|
+
font-weight: inherit;
|
|
645
|
+
color: inherit;
|
|
646
|
+
line-height: inherit;
|
|
647
|
+
text-align: inherit;
|
|
648
|
+
white-space: nowrap;
|
|
649
|
+
overflow: hidden;
|
|
650
|
+
text-overflow: ellipsis;
|
|
651
|
+
pointer-events: none;
|
|
652
|
+
z-index: 1;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.input__link-text :global(a) {
|
|
656
|
+
pointer-events: auto;
|
|
657
|
+
text-decoration: underline;
|
|
658
|
+
}
|
|
659
|
+
|
|
616
660
|
.input__clear-button {
|
|
617
661
|
position: absolute;
|
|
618
662
|
top: 50%;
|
|
@@ -695,7 +739,6 @@
|
|
|
695
739
|
|
|
696
740
|
input {
|
|
697
741
|
min-height: var(--svelte-ui-input-height);
|
|
698
|
-
padding: var(--svelte-ui-input-padding);
|
|
699
742
|
background-color: var(--svelte-ui-input-bg);
|
|
700
743
|
box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-input-border-color);
|
|
701
744
|
border: none;
|
|
@@ -703,30 +746,40 @@
|
|
|
703
746
|
color: var(--svelte-ui-input-text-color);
|
|
704
747
|
}
|
|
705
748
|
|
|
749
|
+
input,
|
|
750
|
+
.input__display-text,
|
|
751
|
+
.input__link-text {
|
|
752
|
+
padding: var(--svelte-ui-input-padding);
|
|
753
|
+
}
|
|
754
|
+
|
|
706
755
|
&.input--has-left-icon {
|
|
707
756
|
input,
|
|
708
|
-
.input__display-text
|
|
757
|
+
.input__display-text,
|
|
758
|
+
.input__link-text {
|
|
709
759
|
padding-left: var(--svelte-ui-input-icon-space);
|
|
710
760
|
}
|
|
711
761
|
}
|
|
712
762
|
|
|
713
763
|
&.input--has-right-icon {
|
|
714
764
|
input,
|
|
715
|
-
.input__display-text
|
|
765
|
+
.input__display-text,
|
|
766
|
+
.input__link-text {
|
|
716
767
|
padding-right: var(--svelte-ui-input-icon-space);
|
|
717
768
|
}
|
|
718
769
|
}
|
|
719
770
|
|
|
720
771
|
&.input--clearable {
|
|
721
772
|
input,
|
|
722
|
-
.input__display-text
|
|
773
|
+
.input__display-text,
|
|
774
|
+
.input__link-text {
|
|
723
775
|
padding-right: var(--svelte-ui-input-icon-space);
|
|
724
776
|
}
|
|
725
777
|
}
|
|
726
778
|
|
|
727
779
|
&.input--clearable.input--has-right-icon {
|
|
728
780
|
input,
|
|
729
|
-
.input__display-text
|
|
781
|
+
.input__display-text,
|
|
782
|
+
.input__link-text {
|
|
730
783
|
padding-right: var(--svelte-ui-input-icon-space-double);
|
|
731
784
|
}
|
|
732
785
|
}
|
|
@@ -741,6 +794,18 @@
|
|
|
741
794
|
}
|
|
742
795
|
}
|
|
743
796
|
|
|
797
|
+
/* linkify=true かつフォーカスがないときは、input のテキストカラーだけ透明にして二重描画を防ぐ */
|
|
798
|
+
.input--linkify:not(.input--focused) input {
|
|
799
|
+
color: transparent;
|
|
800
|
+
caret-color: transparent;
|
|
801
|
+
text-shadow: none;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/* フォーカス時はリンク用オーバーレイも非表示にして(display:none)、リンクが反応しないようにする */
|
|
805
|
+
.input--focused .input__link-text {
|
|
806
|
+
display: none;
|
|
807
|
+
}
|
|
808
|
+
|
|
744
809
|
/* =============================================
|
|
745
810
|
* デザインバリアント:inline
|
|
746
811
|
* ============================================= */
|
|
@@ -759,28 +824,32 @@
|
|
|
759
824
|
|
|
760
825
|
&.input--has-left-icon {
|
|
761
826
|
input,
|
|
762
|
-
.input__display-text
|
|
827
|
+
.input__display-text,
|
|
828
|
+
.input__link-text {
|
|
763
829
|
padding-left: var(--svelte-ui-input-icon-space-inline);
|
|
764
830
|
}
|
|
765
831
|
}
|
|
766
832
|
|
|
767
833
|
&.input--has-right-icon {
|
|
768
834
|
input,
|
|
769
|
-
.input__display-text
|
|
835
|
+
.input__display-text,
|
|
836
|
+
.input__link-text {
|
|
770
837
|
padding-right: var(--svelte-ui-input-icon-space-inline);
|
|
771
838
|
}
|
|
772
839
|
}
|
|
773
840
|
|
|
774
841
|
&.input--clearable {
|
|
775
842
|
input,
|
|
776
|
-
.input__display-text
|
|
843
|
+
.input__display-text,
|
|
844
|
+
.input__link-text {
|
|
777
845
|
padding-right: var(--svelte-ui-input-icon-space-inline);
|
|
778
846
|
}
|
|
779
847
|
}
|
|
780
848
|
|
|
781
849
|
&.input--clearable.input--has-right-icon {
|
|
782
850
|
input,
|
|
783
|
-
.input__display-text
|
|
851
|
+
.input__display-text,
|
|
852
|
+
.input__link-text {
|
|
784
853
|
padding-right: var(--svelte-ui-input-icon-space-double-inline);
|
|
785
854
|
}
|
|
786
855
|
}
|
|
@@ -796,6 +865,15 @@
|
|
|
796
865
|
}
|
|
797
866
|
}
|
|
798
867
|
|
|
868
|
+
/* inline + linkify のときは、display-text を常に隠し、wrapper を常に表示 */
|
|
869
|
+
.input--inline.input--linkify .input__display-text {
|
|
870
|
+
opacity: 0;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.input--inline.input--linkify .input__wrapper {
|
|
874
|
+
opacity: 1;
|
|
875
|
+
}
|
|
876
|
+
|
|
799
877
|
/* =============================================
|
|
800
878
|
* レイアウトバリエーション
|
|
801
879
|
* ============================================= */
|
|
@@ -812,21 +890,24 @@
|
|
|
812
890
|
* ============================================= */
|
|
813
891
|
.input--clearable {
|
|
814
892
|
input,
|
|
815
|
-
.input__display-text
|
|
893
|
+
.input__display-text,
|
|
894
|
+
.input__link-text {
|
|
816
895
|
padding-right: var(--svelte-ui-input-icon-space);
|
|
817
896
|
}
|
|
818
897
|
}
|
|
819
898
|
|
|
820
899
|
.input.input--has-right-icon {
|
|
821
900
|
input,
|
|
822
|
-
.input__display-text
|
|
901
|
+
.input__display-text,
|
|
902
|
+
.input__link-text {
|
|
823
903
|
padding-right: var(--svelte-ui-input-icon-space);
|
|
824
904
|
}
|
|
825
905
|
}
|
|
826
906
|
|
|
827
907
|
.input.input--has-left-icon {
|
|
828
908
|
input,
|
|
829
|
-
.input__display-text
|
|
909
|
+
.input__display-text,
|
|
910
|
+
.input__link-text {
|
|
830
911
|
padding-left: var(--svelte-ui-input-icon-space);
|
|
831
912
|
}
|
|
832
913
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import IconButton from './IconButton.svelte';
|
|
5
5
|
import { getStyleFromNumber } from '../utils/style';
|
|
6
6
|
import { t } from '../i18n';
|
|
7
|
+
import { convertToHtml, convertToHtmlWithLink } from '../utils/formatText';
|
|
7
8
|
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
|
8
9
|
import type { IconVariant } from '../types/icon';
|
|
9
10
|
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
readonly = false,
|
|
49
50
|
required = false,
|
|
50
51
|
iconVariant = 'outlined',
|
|
52
|
+
linkify = false,
|
|
51
53
|
|
|
52
54
|
// フォーカスイベント
|
|
53
55
|
onfocus = () => {}, // No params for type inference
|
|
@@ -125,6 +127,7 @@
|
|
|
125
127
|
readonly?: boolean;
|
|
126
128
|
required?: boolean;
|
|
127
129
|
iconVariant?: IconVariant;
|
|
130
|
+
linkify?: boolean;
|
|
128
131
|
|
|
129
132
|
// フォーカスイベント
|
|
130
133
|
onfocus?: Function; // No params for type inference
|
|
@@ -335,9 +338,7 @@
|
|
|
335
338
|
// HTML表示用の値(autoResize時の高さ調整用)
|
|
336
339
|
const htmlValue = $derived.by(() => {
|
|
337
340
|
if (value !== '') {
|
|
338
|
-
let html = value
|
|
339
|
-
.replace(/ +/g, (match) => ' '.repeat(match.length))
|
|
340
|
-
.replace(/\n/g, '<br />');
|
|
341
|
+
let html = convertToHtml(value) as string;
|
|
341
342
|
// 最後の行が空だったら空白を追加(高さ調整のため)
|
|
342
343
|
const lines = html.split('<br />');
|
|
343
344
|
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
@@ -348,12 +349,22 @@
|
|
|
348
349
|
return '';
|
|
349
350
|
}
|
|
350
351
|
});
|
|
352
|
+
|
|
353
|
+
// URLをリンク化した表示用HTML(クリック検出用オーバーレイで使用)
|
|
354
|
+
const linkHtmlValue = $derived.by(() => {
|
|
355
|
+
if (!linkify || value === '') {
|
|
356
|
+
return '';
|
|
357
|
+
}
|
|
358
|
+
const result = convertToHtmlWithLink(value);
|
|
359
|
+
return typeof result === 'string' ? result : String(result ?? '');
|
|
360
|
+
});
|
|
351
361
|
</script>
|
|
352
362
|
|
|
353
363
|
<div
|
|
354
364
|
class="textarea
|
|
355
365
|
textarea--focus-{focusStyle}"
|
|
356
366
|
class:textarea--inline={inline}
|
|
367
|
+
class:textarea--linkify={linkify}
|
|
357
368
|
class:textarea--full-width={fullWidth}
|
|
358
369
|
class:textarea--full-height={fullHeight}
|
|
359
370
|
class:textarea--auto-resize={autoResize}
|
|
@@ -438,6 +449,11 @@
|
|
|
438
449
|
</div>
|
|
439
450
|
{/if}
|
|
440
451
|
</div>
|
|
452
|
+
{#if linkify}
|
|
453
|
+
<div class="textarea__link-text" style="{minHeightStyle} {customStyle}">
|
|
454
|
+
{@html linkHtmlValue}
|
|
455
|
+
</div>
|
|
456
|
+
{/if}
|
|
441
457
|
</div>
|
|
442
458
|
|
|
443
459
|
<style>
|
|
@@ -471,11 +487,11 @@
|
|
|
471
487
|
}
|
|
472
488
|
|
|
473
489
|
/* =============================================
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
.textarea__display-text
|
|
477
|
-
|
|
478
|
-
|
|
490
|
+
* 基本コンポーネント
|
|
491
|
+
* ============================================= */
|
|
492
|
+
.textarea__display-text,
|
|
493
|
+
.textarea__link-text {
|
|
494
|
+
display: block;
|
|
479
495
|
width: 100%;
|
|
480
496
|
background: inherit;
|
|
481
497
|
border: inherit;
|
|
@@ -498,6 +514,23 @@
|
|
|
498
514
|
}
|
|
499
515
|
}
|
|
500
516
|
|
|
517
|
+
/* クリック可能なリンク用オーバーレイ */
|
|
518
|
+
.textarea__link-text {
|
|
519
|
+
position: absolute;
|
|
520
|
+
top: 0;
|
|
521
|
+
left: 0;
|
|
522
|
+
width: 100%;
|
|
523
|
+
height: 100%;
|
|
524
|
+
padding: inherit;
|
|
525
|
+
pointer-events: none;
|
|
526
|
+
z-index: 1;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.textarea__link-text :global(a) {
|
|
530
|
+
pointer-events: auto;
|
|
531
|
+
text-decoration: underline;
|
|
532
|
+
}
|
|
533
|
+
|
|
501
534
|
textarea {
|
|
502
535
|
position: absolute;
|
|
503
536
|
top: 0;
|
|
@@ -626,8 +659,8 @@
|
|
|
626
659
|
}
|
|
627
660
|
|
|
628
661
|
/* =============================================
|
|
629
|
-
|
|
630
|
-
|
|
662
|
+
* 表示切り替え(フォーカス時・非inline)
|
|
663
|
+
* ============================================= */
|
|
631
664
|
.textarea--focused,
|
|
632
665
|
.textarea:not(.textarea--inline) {
|
|
633
666
|
.textarea__display-text {
|
|
@@ -639,11 +672,22 @@
|
|
|
639
672
|
}
|
|
640
673
|
}
|
|
641
674
|
|
|
675
|
+
/* linkify=true かつ非 inline のときは、display-text は常に非表示(レイアウトだけ保持) */
|
|
676
|
+
.textarea--linkify:not(.textarea--inline) .textarea__display-text {
|
|
677
|
+
opacity: 0;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/* フォーカス時はリンク用オーバーレイも非表示にして(display:none)、リンクが反応しないようにする */
|
|
681
|
+
.textarea--focused .textarea__link-text {
|
|
682
|
+
display: none;
|
|
683
|
+
}
|
|
684
|
+
|
|
642
685
|
/* =============================================
|
|
643
686
|
* デザインバリアント:default
|
|
644
687
|
* ============================================= */
|
|
645
688
|
.textarea:not(.textarea--inline) {
|
|
646
|
-
.textarea__display-text
|
|
689
|
+
.textarea__display-text,
|
|
690
|
+
.textarea__link-text {
|
|
647
691
|
padding: var(--svelte-ui-textarea-padding);
|
|
648
692
|
}
|
|
649
693
|
|
|
@@ -664,6 +708,15 @@
|
|
|
664
708
|
}
|
|
665
709
|
}
|
|
666
710
|
|
|
711
|
+
/* linkify=true かつフォーカスがないときは、textarea のテキストカラーだけ透明にして二重描画を防ぐ
|
|
712
|
+
* placeholder の色は textarea::placeholder 側で指定しているため、この指定の影響を受けない
|
|
713
|
+
*/
|
|
714
|
+
.textarea--linkify:not(.textarea--focused) textarea {
|
|
715
|
+
color: transparent;
|
|
716
|
+
caret-color: transparent;
|
|
717
|
+
text-shadow: none;
|
|
718
|
+
}
|
|
719
|
+
|
|
667
720
|
/* =============================================
|
|
668
721
|
* デザインバリアント:rounded
|
|
669
722
|
* ============================================= */
|
|
@@ -699,4 +752,13 @@
|
|
|
699
752
|
top: var(--svelte-ui-textarea-icon-top-inline);
|
|
700
753
|
}
|
|
701
754
|
}
|
|
755
|
+
|
|
756
|
+
/* inline + linkify のときは、display-text を常に隠し、textarea を常に表示 */
|
|
757
|
+
.textarea--inline.textarea--linkify .textarea__display-text {
|
|
758
|
+
opacity: 0;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.textarea--inline.textarea--linkify textarea {
|
|
762
|
+
opacity: 1;
|
|
763
|
+
}
|
|
702
764
|
</style>
|