vanilla-jui 1.5.4 → 1.5.6
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/README.md +0 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +5 -5
- package/src/components/accordion.ts +5 -4
- package/src/components/flow.ts +108 -93
- package/src/components/form.ts +183 -118
- package/src/components/modal.ts +63 -56
- package/src/components/offcanvas.ts +8 -6
- package/src/components/swiper.ts +45 -39
- package/src/components/tabs.ts +3 -3
- package/src/css/content.css +8 -3
- package/src/locales/index.ts +2 -0
- package/src/primitives/drop.ts +2 -1
- package/src/primitives/popup.ts +4 -4
- package/src/utilities/dom.ts +7 -0
package/src/components/form.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
For,
|
|
3
|
+
type ElementProps,
|
|
3
4
|
createDeepStore,
|
|
4
5
|
createEffect,
|
|
5
6
|
createMemo,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
defineComponent,
|
|
14
15
|
} from '../core/component.ts';
|
|
15
16
|
import { joinClasses } from '../utilities/class-name.ts';
|
|
16
|
-
import { type RenderableContent, all } from '../utilities/dom.ts';
|
|
17
|
+
import { asRenderable, type RenderableContent, all } from '../utilities/dom.ts';
|
|
17
18
|
import { randomId } from '../utilities/id.ts';
|
|
18
19
|
import { isPlainObject } from '../utilities/object.ts';
|
|
19
20
|
import {
|
|
@@ -23,6 +24,8 @@ import {
|
|
|
23
24
|
} from '../utilities/types.ts';
|
|
24
25
|
import { createValidator } from '../validation/validator.ts';
|
|
25
26
|
import { createLoading } from '../primitives/loading.ts';
|
|
27
|
+
import { t } from 'vanilla-signal-i18n';
|
|
28
|
+
import locales from '../locales/index.ts';
|
|
26
29
|
|
|
27
30
|
type FormValue = string | number | boolean;
|
|
28
31
|
export type FieldOption = FormValue | FormOption;
|
|
@@ -99,7 +102,10 @@ export interface FormField {
|
|
|
99
102
|
label?: RenderableContent<Form> | false;
|
|
100
103
|
name?: string;
|
|
101
104
|
options?: readonly FieldOption[];
|
|
102
|
-
value?:
|
|
105
|
+
value?:
|
|
106
|
+
| FormDataEntryValue
|
|
107
|
+
| FormValue
|
|
108
|
+
| readonly (FormDataEntryValue | FormValue)[];
|
|
103
109
|
checked?: boolean;
|
|
104
110
|
required?: boolean;
|
|
105
111
|
placeholder?: string;
|
|
@@ -230,8 +236,18 @@ const DEFAULT_CLASS_NAMES: FormClassNames = {
|
|
|
230
236
|
};
|
|
231
237
|
|
|
232
238
|
const DEFAULT_BUTTONS: FormButton[] = [
|
|
233
|
-
{
|
|
234
|
-
|
|
239
|
+
{
|
|
240
|
+
type: 'submit',
|
|
241
|
+
text: t('Submit', locales),
|
|
242
|
+
theme: 'primary',
|
|
243
|
+
action: 'submit',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
type: 'reset',
|
|
247
|
+
text: t('Reset', locales),
|
|
248
|
+
theme: 'ghost',
|
|
249
|
+
action: 'reset',
|
|
250
|
+
},
|
|
235
251
|
];
|
|
236
252
|
|
|
237
253
|
// Guard against malformed dynamic chains that never terminate.
|
|
@@ -365,7 +381,21 @@ function stringifyFormValue(
|
|
|
365
381
|
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
366
382
|
return String(value);
|
|
367
383
|
}
|
|
368
|
-
return value.name;
|
|
384
|
+
if (value instanceof File) return value.name;
|
|
385
|
+
return '';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function stringifyControlValue(value: FormField['value'] | undefined): string {
|
|
389
|
+
if (value == null || Array.isArray(value)) return '';
|
|
390
|
+
if (
|
|
391
|
+
typeof value === 'string' ||
|
|
392
|
+
typeof value === 'number' ||
|
|
393
|
+
typeof value === 'boolean'
|
|
394
|
+
) {
|
|
395
|
+
return String(value);
|
|
396
|
+
}
|
|
397
|
+
if (value instanceof File) return value.name;
|
|
398
|
+
return '';
|
|
369
399
|
}
|
|
370
400
|
|
|
371
401
|
function fieldIsRequired(
|
|
@@ -687,97 +717,112 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
687
717
|
setChainVersion((version: number) => version + 1);
|
|
688
718
|
};
|
|
689
719
|
|
|
690
|
-
const controlProps = <
|
|
691
|
-
|
|
720
|
+
const controlProps = <TElement extends Element>(
|
|
721
|
+
fieldAccessor: () => FormField,
|
|
692
722
|
id: string,
|
|
693
|
-
extra:
|
|
694
|
-
):
|
|
723
|
+
extra: ElementProps<TElement>
|
|
724
|
+
): ElementProps<TElement> => ({
|
|
695
725
|
...extra,
|
|
696
|
-
name:
|
|
726
|
+
name: () => fieldAccessor().name,
|
|
697
727
|
id,
|
|
698
|
-
placeholder:
|
|
699
|
-
required: !!
|
|
700
|
-
disabled: () => state.submitting || !!
|
|
701
|
-
readonly: !!
|
|
702
|
-
'data-form-field':
|
|
728
|
+
placeholder: () => fieldAccessor().placeholder || '',
|
|
729
|
+
required: () => !!fieldAccessor().required,
|
|
730
|
+
disabled: () => state.submitting || !!fieldAccessor().disabled,
|
|
731
|
+
readonly: () => !!fieldAccessor().readonly,
|
|
732
|
+
'data-form-field': () => fieldAccessor().name || id,
|
|
703
733
|
});
|
|
704
734
|
|
|
705
735
|
const choiceGroupView = (
|
|
706
|
-
|
|
736
|
+
fieldAccessor: () => FormField,
|
|
707
737
|
id: string,
|
|
708
738
|
type: 'checkbox' | 'radio'
|
|
709
739
|
): HTMLElement => {
|
|
710
|
-
const direction = field.vertical ? 'vertical' : 'horizontal';
|
|
711
|
-
const names = state.className;
|
|
712
740
|
return jsx('div', {
|
|
713
|
-
className:
|
|
714
|
-
|
|
715
|
-
direction
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
741
|
+
className: () => {
|
|
742
|
+
const field = fieldAccessor();
|
|
743
|
+
const direction = field.vertical ? 'vertical' : 'horizontal';
|
|
744
|
+
const names = state.className;
|
|
745
|
+
return joinClasses(
|
|
746
|
+
type === 'radio' ? names.radio : names.checkbox,
|
|
747
|
+
direction === 'vertical'
|
|
748
|
+
? names.choiceVertical
|
|
749
|
+
: names.choiceHorizontal,
|
|
750
|
+
field.group ? names.choiceGroup : '',
|
|
751
|
+
field.size ? `is-${field.size}` : ''
|
|
752
|
+
);
|
|
753
|
+
},
|
|
754
|
+
'data-form-choice-group': () => fieldAccessor().name || id,
|
|
722
755
|
'data-choice-type': type,
|
|
723
|
-
'data-choice-layout':
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
'
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
756
|
+
'data-choice-layout': () =>
|
|
757
|
+
fieldAccessor().vertical ? 'vertical' : 'horizontal',
|
|
758
|
+
children: () =>
|
|
759
|
+
(fieldAccessor().options || []).map((option, optionIndex) => {
|
|
760
|
+
const item = normalizeOption(option);
|
|
761
|
+
const optionId = `${id}_${optionIndex}`;
|
|
762
|
+
return jsx('label', {
|
|
763
|
+
className: () =>
|
|
764
|
+
type === 'radio' ? state.className.radioLabel : '',
|
|
765
|
+
'data-form-choice': () => fieldAccessor().name || id,
|
|
766
|
+
for: optionId,
|
|
767
|
+
children: [
|
|
768
|
+
jsx('input', {
|
|
769
|
+
type,
|
|
770
|
+
id: optionId,
|
|
771
|
+
name: () => fieldAccessor().name,
|
|
772
|
+
value: item.value ?? '',
|
|
773
|
+
checked: () =>
|
|
774
|
+
type === 'radio'
|
|
775
|
+
? isSelected(fieldAccessor().value, item.value)
|
|
776
|
+
: isChecked(
|
|
777
|
+
fieldAccessor().value,
|
|
778
|
+
item.value,
|
|
779
|
+
item.checked
|
|
780
|
+
),
|
|
781
|
+
disabled: () =>
|
|
782
|
+
state.submitting ||
|
|
783
|
+
!!item.disabled ||
|
|
784
|
+
!!fieldAccessor().disabled,
|
|
785
|
+
required: () => !!fieldAccessor().required,
|
|
786
|
+
onChange: (event: Event) => {
|
|
787
|
+
const target = event.currentTarget;
|
|
788
|
+
if (target instanceof HTMLInputElement) {
|
|
789
|
+
syncFieldValue(fieldAccessor(), target, type);
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
}),
|
|
793
|
+
jsx('span', {
|
|
794
|
+
className: () =>
|
|
795
|
+
type === 'radio' ? state.className.radioText : '',
|
|
796
|
+
'data-form-choice-text': '',
|
|
797
|
+
children: asRenderable(
|
|
798
|
+
item.text ?? item.label ?? stringifyFormValue(item.value)
|
|
799
|
+
),
|
|
800
|
+
}),
|
|
801
|
+
],
|
|
802
|
+
});
|
|
803
|
+
}),
|
|
759
804
|
}) as HTMLElement;
|
|
760
805
|
};
|
|
761
806
|
|
|
762
807
|
const controlView = (
|
|
763
|
-
|
|
764
|
-
|
|
808
|
+
itemAccessor: () => FormItem<FormField>,
|
|
809
|
+
fieldAccessor: () => FormField,
|
|
765
810
|
id: string,
|
|
766
|
-
|
|
767
|
-
item: FormItem<FormField>
|
|
811
|
+
indexAccessor: () => number
|
|
768
812
|
): RenderableContent<FormControlContext> => {
|
|
769
|
-
const
|
|
813
|
+
const type = itemAccessor().type;
|
|
770
814
|
if (type === 'textarea') {
|
|
771
815
|
return jsx(
|
|
772
816
|
'textarea',
|
|
773
|
-
controlProps(
|
|
774
|
-
className:
|
|
775
|
-
|
|
776
|
-
|
|
817
|
+
controlProps<HTMLTextAreaElement>(fieldAccessor, id, {
|
|
818
|
+
className: () =>
|
|
819
|
+
fieldAccessor().className || state.className.textarea,
|
|
820
|
+
autocomplete: () => fieldAccessor().autocomplete,
|
|
821
|
+
value: () => stringifyControlValue(fieldAccessor().value),
|
|
777
822
|
onInput: (event: Event) => {
|
|
778
823
|
const target = event.currentTarget;
|
|
779
824
|
if (target instanceof HTMLTextAreaElement) {
|
|
780
|
-
syncFieldValue(
|
|
825
|
+
syncFieldValue(fieldAccessor(), target, 'textarea');
|
|
781
826
|
}
|
|
782
827
|
},
|
|
783
828
|
})
|
|
@@ -786,84 +831,105 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
786
831
|
if (type === 'select') {
|
|
787
832
|
return jsx(
|
|
788
833
|
'select',
|
|
789
|
-
controlProps(
|
|
790
|
-
className:
|
|
791
|
-
autocomplete:
|
|
792
|
-
multiple: !!
|
|
834
|
+
controlProps<HTMLSelectElement>(fieldAccessor, id, {
|
|
835
|
+
className: () => fieldAccessor().className || state.className.select,
|
|
836
|
+
autocomplete: () => fieldAccessor().autocomplete || 'off',
|
|
837
|
+
multiple: () => !!fieldAccessor().multiple,
|
|
838
|
+
value: () =>
|
|
839
|
+
fieldAccessor().multiple
|
|
840
|
+
? undefined
|
|
841
|
+
: stringifyControlValue(fieldAccessor().value),
|
|
793
842
|
onChange: (event: Event) => {
|
|
794
843
|
const target = event.currentTarget;
|
|
795
844
|
if (target instanceof HTMLSelectElement) {
|
|
796
|
-
syncFieldValue(
|
|
845
|
+
syncFieldValue(fieldAccessor(), target, 'select');
|
|
797
846
|
}
|
|
798
847
|
},
|
|
799
|
-
children: (
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
848
|
+
children: () =>
|
|
849
|
+
(fieldAccessor().options || []).map((option) => {
|
|
850
|
+
const item = normalizeOption(option);
|
|
851
|
+
return jsx('option', {
|
|
852
|
+
value: item.value ?? '',
|
|
853
|
+
disabled: !!item.disabled,
|
|
854
|
+
selected: () => isSelected(fieldAccessor().value, item.value),
|
|
855
|
+
children: asRenderable(
|
|
856
|
+
item.text ?? item.label ?? stringifyFormValue(item.value)
|
|
857
|
+
),
|
|
858
|
+
});
|
|
859
|
+
}),
|
|
808
860
|
})
|
|
809
861
|
) as HTMLSelectElement;
|
|
810
862
|
}
|
|
811
|
-
if (type === 'radio') return choiceGroupView(
|
|
812
|
-
if (type === 'checkbox' && Array.isArray(
|
|
813
|
-
return choiceGroupView(
|
|
863
|
+
if (type === 'radio') return choiceGroupView(fieldAccessor, id, 'radio');
|
|
864
|
+
if (type === 'checkbox' && Array.isArray(fieldAccessor().options)) {
|
|
865
|
+
return choiceGroupView(fieldAccessor, id, 'checkbox');
|
|
814
866
|
}
|
|
815
867
|
if (type === 'switch') {
|
|
816
868
|
return jsx('label', {
|
|
817
|
-
className:
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
869
|
+
className: () => {
|
|
870
|
+
const field = fieldAccessor();
|
|
871
|
+
const names = state.className;
|
|
872
|
+
return joinClasses(
|
|
873
|
+
names.switch,
|
|
874
|
+
field.variant ? `is-${field.variant}` : names.switchDefault,
|
|
875
|
+
field.size ? `is-${field.size}` : names.switchSizeMd
|
|
876
|
+
);
|
|
877
|
+
},
|
|
878
|
+
'data-form-switch': () => fieldAccessor().name || id,
|
|
823
879
|
for: id,
|
|
824
880
|
children: [
|
|
825
881
|
jsx('input', {
|
|
826
882
|
type: 'checkbox',
|
|
827
883
|
id,
|
|
828
|
-
name:
|
|
829
|
-
value:
|
|
830
|
-
checked: !!
|
|
831
|
-
disabled: () => state.submitting || !!
|
|
832
|
-
required: !!
|
|
884
|
+
name: () => fieldAccessor().name,
|
|
885
|
+
value: () => fieldAccessor().value ?? '1',
|
|
886
|
+
checked: () => !!fieldAccessor().checked,
|
|
887
|
+
disabled: () => state.submitting || !!fieldAccessor().disabled,
|
|
888
|
+
required: () => !!fieldAccessor().required,
|
|
833
889
|
onChange: (event: Event) => {
|
|
834
890
|
const target = event.currentTarget;
|
|
835
891
|
if (target instanceof HTMLInputElement) {
|
|
836
|
-
syncFieldValue(
|
|
892
|
+
syncFieldValue(fieldAccessor(), target, 'switch');
|
|
837
893
|
}
|
|
838
894
|
},
|
|
839
895
|
}),
|
|
840
896
|
jsx('span', {
|
|
841
|
-
className:
|
|
897
|
+
className: () => state.className.switchSlider,
|
|
842
898
|
'data-form-switch-slider': '',
|
|
843
899
|
}),
|
|
844
900
|
],
|
|
845
901
|
}) as HTMLLabelElement;
|
|
846
902
|
}
|
|
847
903
|
if (type === 'custom') {
|
|
904
|
+
const field = fieldAccessor();
|
|
848
905
|
return typeof field.content === 'function'
|
|
849
|
-
? field.content({
|
|
906
|
+
? field.content({
|
|
907
|
+
form,
|
|
908
|
+
field,
|
|
909
|
+
index: indexAccessor(),
|
|
910
|
+
item: itemAccessor(),
|
|
911
|
+
})
|
|
850
912
|
: field.content;
|
|
851
913
|
}
|
|
852
914
|
const inputType = type || 'text';
|
|
853
915
|
return jsx('input', {
|
|
854
|
-
...controlProps(
|
|
916
|
+
...controlProps<HTMLInputElement>(fieldAccessor, id, {
|
|
855
917
|
type: inputType,
|
|
856
|
-
className:
|
|
857
|
-
autocomplete:
|
|
858
|
-
|
|
918
|
+
className: () => fieldAccessor().className || state.className.input,
|
|
919
|
+
autocomplete: () =>
|
|
920
|
+
fieldAccessor().autocomplete || autoComplete(inputType),
|
|
921
|
+
value: () => fieldAccessor().value ?? '',
|
|
859
922
|
onInput: (event: Event) => {
|
|
860
923
|
const target = event.currentTarget;
|
|
861
924
|
if (target instanceof HTMLInputElement) {
|
|
862
|
-
syncFieldValue(
|
|
925
|
+
syncFieldValue(fieldAccessor(), target, inputType);
|
|
863
926
|
}
|
|
864
927
|
},
|
|
865
928
|
}),
|
|
866
|
-
checked:
|
|
929
|
+
checked: () => {
|
|
930
|
+
const checked = fieldAccessor().checked;
|
|
931
|
+
return checked === undefined ? undefined : !!checked;
|
|
932
|
+
},
|
|
867
933
|
}) as HTMLInputElement;
|
|
868
934
|
};
|
|
869
935
|
|
|
@@ -871,11 +937,8 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
871
937
|
itemAccessor: () => FormItem<FormField>,
|
|
872
938
|
indexAccessor: () => number
|
|
873
939
|
): HTMLElement => {
|
|
874
|
-
const item = itemAccessor();
|
|
875
|
-
const field = item.payload;
|
|
876
940
|
const fieldAccessor = (): FormField => itemAccessor().payload;
|
|
877
|
-
const
|
|
878
|
-
const id = resolveFieldId(field, index);
|
|
941
|
+
const id = resolveFieldId(fieldAccessor(), indexAccessor());
|
|
879
942
|
return jsx('div', {
|
|
880
943
|
className: () => state.className.item,
|
|
881
944
|
'data-form-item': () => fieldAccessor().name || String(indexAccessor()),
|
|
@@ -898,7 +961,7 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
898
961
|
),
|
|
899
962
|
'data-form-label': () => fieldAccessor().name || id,
|
|
900
963
|
for: id,
|
|
901
|
-
children: () => fieldAccessor().label,
|
|
964
|
+
children: () => asRenderable(fieldAccessor().label),
|
|
902
965
|
});
|
|
903
966
|
},
|
|
904
967
|
jsx('div', {
|
|
@@ -906,7 +969,9 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
906
969
|
'data-form-control': () =>
|
|
907
970
|
fieldAccessor().name || String(indexAccessor()),
|
|
908
971
|
children: [
|
|
909
|
-
|
|
972
|
+
asRenderable(
|
|
973
|
+
controlView(itemAccessor, fieldAccessor, id, indexAccessor)
|
|
974
|
+
),
|
|
910
975
|
() => {
|
|
911
976
|
const current = fieldAccessor();
|
|
912
977
|
return current.help
|
|
@@ -914,7 +979,7 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
914
979
|
className: 'help-block',
|
|
915
980
|
'data-form-help': () =>
|
|
916
981
|
fieldAccessor().name || String(indexAccessor()),
|
|
917
|
-
children: () => fieldAccessor().help,
|
|
982
|
+
children: () => asRenderable(fieldAccessor().help),
|
|
918
983
|
})
|
|
919
984
|
: null;
|
|
920
985
|
},
|
|
@@ -959,7 +1024,7 @@ export function createForm(input: FormProps = {}): Form {
|
|
|
959
1024
|
disabled: () => state.submitting || !!buttonAccessor().disabled,
|
|
960
1025
|
children: () => {
|
|
961
1026
|
const button = buttonAccessor();
|
|
962
|
-
const content = button.text ?? button.label ?? '';
|
|
1027
|
+
const content = asRenderable(button.text ?? button.label ?? '');
|
|
963
1028
|
const submitting =
|
|
964
1029
|
state.submitting &&
|
|
965
1030
|
(button.type === 'submit' || button.action === 'submit');
|
package/src/components/modal.ts
CHANGED
|
@@ -17,7 +17,12 @@ import { icon } from '../primitives/icons.ts';
|
|
|
17
17
|
import { createLoading } from '../primitives/loading.ts';
|
|
18
18
|
import { createPopup } from '../primitives/popup.ts';
|
|
19
19
|
import { joinClasses } from '../utilities/class-name.ts';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
all,
|
|
22
|
+
asRenderable,
|
|
23
|
+
q,
|
|
24
|
+
type RenderableContent,
|
|
25
|
+
} from '../utilities/dom.ts';
|
|
21
26
|
import { randomId } from '../utilities/id.ts';
|
|
22
27
|
import { createMotionGroup, createTransition } from '../core/motion.ts';
|
|
23
28
|
import { isPlainObject } from '../utilities/object.ts';
|
|
@@ -685,72 +690,74 @@ export function createModal(input: ModalProps = {}): Modal {
|
|
|
685
690
|
...(props.style ? { style: props.style } : {}),
|
|
686
691
|
'data-mount': () => (motionVisible() ? 'true' : 'false'),
|
|
687
692
|
children: [
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
693
|
+
props.header
|
|
694
|
+
? jsx('div', {
|
|
695
|
+
className: props.className.header,
|
|
696
|
+
children: () =>
|
|
697
|
+
state.loading
|
|
698
|
+
? null
|
|
699
|
+
: [
|
|
700
|
+
jsx('div', {
|
|
701
|
+
className: props.className.title,
|
|
702
|
+
id: `${props.id}_title`,
|
|
703
|
+
'aria-label': props.text.title,
|
|
704
|
+
children: props.text.title,
|
|
705
|
+
}),
|
|
706
|
+
props.showClose
|
|
707
|
+
? jsx('button', {
|
|
708
|
+
type: 'button',
|
|
709
|
+
className: joinClasses(
|
|
710
|
+
props.className.button,
|
|
711
|
+
props.className.closeBtn
|
|
712
|
+
),
|
|
713
|
+
'data-action': 'close',
|
|
714
|
+
'aria-label': t('Close', locales),
|
|
715
|
+
disabled: isBusy,
|
|
716
|
+
children: icon('close'),
|
|
717
|
+
})
|
|
718
|
+
: null,
|
|
719
|
+
],
|
|
720
|
+
})
|
|
721
|
+
: null,
|
|
716
722
|
jsx('div', {
|
|
717
723
|
className: props.className.body,
|
|
718
724
|
'data-modal-body': '',
|
|
719
725
|
children: () => {
|
|
720
|
-
return resolvedContent().value;
|
|
726
|
+
return asRenderable(resolvedContent().value);
|
|
721
727
|
},
|
|
722
728
|
}),
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
729
|
+
props.footer
|
|
730
|
+
? jsx('div', {
|
|
731
|
+
className: props.className.footer,
|
|
732
|
+
children: () =>
|
|
733
|
+
state.loading
|
|
734
|
+
? null
|
|
735
|
+
: [
|
|
736
|
+
props.showCancel
|
|
737
|
+
? jsx('button', {
|
|
738
|
+
type: 'button',
|
|
739
|
+
className: joinClasses(
|
|
740
|
+
props.className.button,
|
|
741
|
+
props.className.cancelBtn
|
|
742
|
+
),
|
|
743
|
+
'data-action': 'cancel',
|
|
744
|
+
disabled: isBusy,
|
|
745
|
+
children: props.text.cancel,
|
|
746
|
+
})
|
|
747
|
+
: null,
|
|
748
|
+
jsx('button', {
|
|
731
749
|
type: 'button',
|
|
732
750
|
className: joinClasses(
|
|
733
751
|
props.className.button,
|
|
734
|
-
props.className.
|
|
752
|
+
props.className.confirmBtn
|
|
735
753
|
),
|
|
736
|
-
'data-action': '
|
|
754
|
+
'data-action': 'confirm',
|
|
737
755
|
disabled: isBusy,
|
|
738
|
-
children: props.text.
|
|
739
|
-
})
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
className: joinClasses(
|
|
744
|
-
props.className.button,
|
|
745
|
-
props.className.confirmBtn
|
|
746
|
-
),
|
|
747
|
-
'data-action': 'confirm',
|
|
748
|
-
disabled: isBusy,
|
|
749
|
-
children: props.text.confirm,
|
|
750
|
-
}),
|
|
751
|
-
]
|
|
752
|
-
: null,
|
|
753
|
-
}),
|
|
756
|
+
children: props.text.confirm,
|
|
757
|
+
}),
|
|
758
|
+
],
|
|
759
|
+
})
|
|
760
|
+
: null,
|
|
754
761
|
],
|
|
755
762
|
}) as HTMLElement;
|
|
756
763
|
const root = createPopup({
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
defineComponent,
|
|
13
13
|
} from '../core/component.ts';
|
|
14
14
|
import { createLoading } from '../primitives/loading.ts';
|
|
15
|
-
import { type RenderableContent } from '../utilities/dom.ts';
|
|
15
|
+
import { asRenderable, type RenderableContent } from '../utilities/dom.ts';
|
|
16
16
|
import { randomId } from '../utilities/id.ts';
|
|
17
17
|
import { createEventManager } from '../utilities/events.ts';
|
|
18
18
|
import { createMotionGroup, createTransition } from '../core/motion.ts';
|
|
@@ -448,11 +448,13 @@ export function createOffcanvas(input: OffcanvasProps = {}): Offcanvas {
|
|
|
448
448
|
className: props.className.content,
|
|
449
449
|
'data-offcanvas-content': props.id,
|
|
450
450
|
children: () =>
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
451
|
+
asRenderable(
|
|
452
|
+
state.loading
|
|
453
|
+
? createLoading()
|
|
454
|
+
: typeof state.resolvedContent === 'function'
|
|
455
|
+
? state.resolvedContent(offcanvas)
|
|
456
|
+
: state.resolvedContent
|
|
457
|
+
),
|
|
456
458
|
}),
|
|
457
459
|
}) as HTMLElement;
|
|
458
460
|
},
|