@cloudparker/moldex.js 0.0.87 → 0.0.88
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/dist/services/dialog/dialog-service.d.ts +1 -1
- package/dist/services/dialog/dialog-service.js +2 -1
- package/dist/views/core/dialog/components/picker-dialog/picker-dialog.svelte +71 -37
- package/dist/views/core/dialog/components/picker-dialog/picker-dialog.svelte.d.ts +10 -1
- package/dist/views/core/icon/components/icon-circle/icon-circle.svelte +2 -2
- package/dist/views/core/input/components/combobox-field/combobox-field.svelte +48 -22
- package/dist/views/core/input/components/combobox-field/combobox-field.svelte.d.ts +6 -0
- package/dist/views/core/input/components/phone-field/phone-field.svelte +19 -13
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ export declare function openAlertDialog(params?: DialogProps & {
|
|
|
35
35
|
export declare function openDeleteConfirmDialog({ msg, title, footerOkButtonLable, footerOkButtonClassName, ...params }?: DialogProps & {
|
|
36
36
|
msg?: string;
|
|
37
37
|
}): Promise<unknown>;
|
|
38
|
-
export declare function openPickerDialog<R>({ items, value, multiple, hasCheckbox, hasArrow, maxlength, maxlengthMsg, identityFieldName, titleFieldName, searchFieldName, subtitleFieldName, ...params }: DialogProps & PickerDialogProps): Promise<R>;
|
|
38
|
+
export declare function openPickerDialog<R>({ items, value, multiple, hasCheckbox, hasArrow, maxlength, maxlengthMsg, identityFieldName, titleFieldName, searchFieldName, subtitleFieldName, itemTileSnippet, ...params }: DialogProps & PickerDialogProps): Promise<R>;
|
|
39
39
|
export declare function openNumberFieldDialog({ title, value, label, name, maxlength, fieldClassName, autofocus, required, appearance, size, floatingLabel, ...params }?: DialogProps & InputFieldProps & {
|
|
40
40
|
fieldClassName?: string;
|
|
41
41
|
}): Promise<unknown>;
|
|
@@ -81,7 +81,7 @@ export async function openDeleteConfirmDialog({ msg = 'Are you sure to delete?',
|
|
|
81
81
|
...params,
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
|
-
export async function openPickerDialog({ items, value, multiple, hasCheckbox = true, hasArrow, maxlength, maxlengthMsg, identityFieldName, titleFieldName, searchFieldName, subtitleFieldName, ...params }) {
|
|
84
|
+
export async function openPickerDialog({ items, value, multiple, hasCheckbox = true, hasArrow, maxlength, maxlengthMsg, identityFieldName, titleFieldName, searchFieldName, subtitleFieldName, itemTileSnippet, ...params }) {
|
|
85
85
|
if (hasArrow) {
|
|
86
86
|
multiple = false;
|
|
87
87
|
hasCheckbox = false;
|
|
@@ -100,6 +100,7 @@ export async function openPickerDialog({ items, value, multiple, hasCheckbox = t
|
|
|
100
100
|
titleFieldName,
|
|
101
101
|
searchFieldName,
|
|
102
102
|
subtitleFieldName,
|
|
103
|
+
itemTileSnippet,
|
|
103
104
|
},
|
|
104
105
|
hasHeader: true,
|
|
105
106
|
hasTitle: true,
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<script lang="ts" module></script>
|
|
2
2
|
|
|
3
|
-
<script lang="ts">import { showToast } from "../../../../../services";
|
|
3
|
+
<script lang="ts">import { DialogSizeEnum, getDialogSize, showToast } from "../../../../../services";
|
|
4
4
|
import ButtonListItem from "../../../button/components/button-list-item/button-list-item.svelte";
|
|
5
5
|
import ButtonSearch from "../../../button/components/button-search/button-search.svelte";
|
|
6
|
+
import { onMount } from "svelte";
|
|
7
|
+
import Icon from "../../../icon/components/icon/icon.svelte";
|
|
8
|
+
import { mdiCheckCircle, mdiCheckCircleOutline, mdiChevronRight } from "../../../icon";
|
|
9
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
6
10
|
let {
|
|
7
11
|
value,
|
|
8
12
|
items = [],
|
|
@@ -15,13 +19,23 @@ let {
|
|
|
15
19
|
hasArrow,
|
|
16
20
|
maxlength = 0,
|
|
17
21
|
maxlengthMsg = "Selection limit reached!",
|
|
22
|
+
checkboxIconPath = mdiCheckCircle,
|
|
23
|
+
uncheckboxIconPath = mdiCheckCircleOutline,
|
|
24
|
+
arrowIconPath = mdiChevronRight,
|
|
25
|
+
checkboxIconClassName = "",
|
|
26
|
+
uncheckboxIconClassName = "",
|
|
27
|
+
arrowClassName = "",
|
|
28
|
+
checkboxClassName = "",
|
|
29
|
+
itemTileSnippet,
|
|
18
30
|
closeDialog,
|
|
19
31
|
setResult,
|
|
20
32
|
setOnOkClick,
|
|
21
33
|
setHeaderSnippet,
|
|
22
34
|
setDialogTitle
|
|
23
35
|
} = $props();
|
|
24
|
-
let
|
|
36
|
+
let selectedSet = $state(
|
|
37
|
+
value ? new SvelteSet(Array.isArray(value) ? value : [value]) : new SvelteSet()
|
|
38
|
+
);
|
|
25
39
|
let searchText = $state("");
|
|
26
40
|
let records = $derived.by(() => {
|
|
27
41
|
if (items && typeof items[0] == "string") {
|
|
@@ -42,40 +56,29 @@ let filteredRecords = $derived.by(() => {
|
|
|
42
56
|
}
|
|
43
57
|
return records;
|
|
44
58
|
});
|
|
45
|
-
$effect(() => {
|
|
46
|
-
if (value) {
|
|
47
|
-
if (Array.isArray(value)) {
|
|
48
|
-
value.forEach((val) => selected.items[val] = val);
|
|
49
|
-
} else {
|
|
50
|
-
selected.items[value] = value;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
59
|
function handleItemClick(ev, item, index) {
|
|
55
60
|
let itemId = item[identityFieldName];
|
|
56
61
|
if (!multiple) {
|
|
57
|
-
if (
|
|
58
|
-
|
|
62
|
+
if (selectedSet.has(itemId)) {
|
|
63
|
+
selectedSet.delete(itemId);
|
|
64
|
+
setResult(void 0);
|
|
59
65
|
} else {
|
|
60
|
-
|
|
66
|
+
selectedSet.add(itemId);
|
|
67
|
+
setResult(itemId);
|
|
61
68
|
}
|
|
62
|
-
let selectedItemId = Object.keys(selected.items)[0];
|
|
63
|
-
setResult(selectedItemId);
|
|
64
69
|
closeDialog();
|
|
65
70
|
} else {
|
|
66
|
-
if (
|
|
67
|
-
delete
|
|
71
|
+
if (selectedSet.has(itemId)) {
|
|
72
|
+
selectedSet.delete(itemId);
|
|
68
73
|
} else {
|
|
69
|
-
|
|
70
|
-
if (maxlength > 0 && itemLength2 >= maxlength) {
|
|
74
|
+
if (selectedSet.size >= maxlength) {
|
|
71
75
|
showToast({ msg: maxlengthMsg });
|
|
72
76
|
} else {
|
|
73
|
-
|
|
77
|
+
selectedSet.add(itemId);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
setDialogTitle(`Selected (${itemLength})`);
|
|
80
|
+
if (selectedSet.size) {
|
|
81
|
+
setDialogTitle(`Selected (${selectedSet.size})`);
|
|
79
82
|
} else {
|
|
80
83
|
setDialogTitle("");
|
|
81
84
|
}
|
|
@@ -83,32 +86,63 @@ function handleItemClick(ev, item, index) {
|
|
|
83
86
|
}
|
|
84
87
|
function handleOkClick(ev) {
|
|
85
88
|
if (multiple) {
|
|
86
|
-
closeDialog(
|
|
89
|
+
closeDialog(Array.from(selectedSet));
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
function handleSearch(txt) {
|
|
90
93
|
searchText = txt;
|
|
91
94
|
}
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
onMount(() => {
|
|
96
|
+
setOnOkClick(handleOkClick);
|
|
97
|
+
setHeaderSnippet(headerSnippet);
|
|
98
|
+
});
|
|
94
99
|
</script>
|
|
95
100
|
|
|
96
101
|
{#snippet headerSnippet()}
|
|
97
102
|
<ButtonSearch className="rounded-full !p-3 " onSearch={handleSearch} />
|
|
98
103
|
{/snippet}
|
|
99
104
|
|
|
100
|
-
<div class="mb-4">
|
|
105
|
+
<div class="mb-4 min-h-80">
|
|
101
106
|
{#each filteredRecords as record, index}
|
|
102
|
-
{@const isSelected =
|
|
107
|
+
{@const isSelected = selectedSet.has(record[identityFieldName])}
|
|
103
108
|
<div>
|
|
104
|
-
|
|
105
|
-
onClick={(ev) => handleItemClick(ev, record, index)}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
{#if itemTileSnippet}
|
|
110
|
+
<ButtonListItem onClick={(ev) => handleItemClick(ev, record, index)}>
|
|
111
|
+
{@render itemTileSnippet(record, index)}
|
|
112
|
+
{#if hasCheckbox}
|
|
113
|
+
<div>
|
|
114
|
+
<Icon
|
|
115
|
+
path={isSelected ? checkboxIconPath : uncheckboxIconPath}
|
|
116
|
+
className="w-5 h-5 {checkboxClassName} {isSelected
|
|
117
|
+
? `text-primary ${checkboxIconClassName}`
|
|
118
|
+
: `text-base-400 ${uncheckboxIconClassName}`}"
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
{/if}
|
|
122
|
+
|
|
123
|
+
{#if hasArrow}
|
|
124
|
+
<div>
|
|
125
|
+
<Icon path={arrowIconPath} className="w-5 h-5 text-base-500 {arrowClassName} " />
|
|
126
|
+
</div>
|
|
127
|
+
{/if}
|
|
128
|
+
</ButtonListItem>
|
|
129
|
+
{:else}
|
|
130
|
+
<ButtonListItem
|
|
131
|
+
onClick={(ev) => handleItemClick(ev, record, index)}
|
|
132
|
+
title={record[titleFieldName]}
|
|
133
|
+
subtitle={record[subtitleFieldName || ''] || ''}
|
|
134
|
+
{hasCheckbox}
|
|
135
|
+
{hasArrow}
|
|
136
|
+
isChecked={isSelected}
|
|
137
|
+
{checkboxIconPath}
|
|
138
|
+
{uncheckboxIconPath}
|
|
139
|
+
{checkboxIconClassName}
|
|
140
|
+
{uncheckboxIconClassName}
|
|
141
|
+
{checkboxClassName}
|
|
142
|
+
{arrowIconPath}
|
|
143
|
+
{arrowClassName}
|
|
144
|
+
/>
|
|
145
|
+
{/if}
|
|
112
146
|
</div>
|
|
113
147
|
{/each}
|
|
114
148
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type PickerDialogProps = {
|
|
2
2
|
items?: string[] | any[];
|
|
3
3
|
multiple?: boolean;
|
|
4
|
-
value?:
|
|
4
|
+
value?: any;
|
|
5
5
|
identityFieldName?: string;
|
|
6
6
|
titleFieldName?: string;
|
|
7
7
|
searchFieldName?: string;
|
|
@@ -10,7 +10,16 @@ export type PickerDialogProps = {
|
|
|
10
10
|
hasArrow?: boolean;
|
|
11
11
|
maxlength?: number;
|
|
12
12
|
maxlengthMsg?: string;
|
|
13
|
+
checkboxIconPath?: string;
|
|
14
|
+
uncheckboxIconPath?: string;
|
|
15
|
+
checkboxIconClassName?: string;
|
|
16
|
+
uncheckboxIconClassName?: string;
|
|
17
|
+
checkboxClassName?: string;
|
|
18
|
+
arrowIconPath?: string;
|
|
19
|
+
arrowClassName?: string;
|
|
20
|
+
itemTileSnippet?: Snippet<[item: any, index: number]>;
|
|
13
21
|
};
|
|
22
|
+
import { type Snippet } from 'svelte';
|
|
14
23
|
import type { DialogExports } from '../dialog/dialog.svelte';
|
|
15
24
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
16
25
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">import Icon from "../icon/icon.svelte";
|
|
2
|
-
let { circleClassName, iconPath, iconClassName = "text-primary" } = $props();
|
|
2
|
+
let { circleClassName, iconPath, iconClassName = "w-5 h-5 text-primary" } = $props();
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<div
|
|
6
|
-
class="aspect-square bg-primary-100 rounded-full w-
|
|
6
|
+
class="aspect-square bg-primary-100 rounded-full w-10 h-10 flex items-center justify-center {circleClassName}"
|
|
7
7
|
>
|
|
8
8
|
<Icon path={iconPath!} className={iconClassName} />
|
|
9
9
|
</div>
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
<script lang="ts">import ButtonListItem from "../../../button/components/button-list-item/button-list-item.svelte";
|
|
4
4
|
import Button from "../../../button/components/button/button.svelte";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
mdiCheckCircle,
|
|
7
|
+
mdiCheckCircleOutline,
|
|
8
|
+
mdiUnfoldMoreHorizontal
|
|
9
|
+
} from "../../../icon";
|
|
6
10
|
import Icon from "../../../icon/components/icon/icon.svelte";
|
|
7
11
|
import NoData from "../../../no-data/components/no-data/no-data.svelte";
|
|
8
12
|
import InputField, {} from "../input-field/input-field.svelte";
|
|
9
13
|
import SearchField from "../search-field/search-field.svelte";
|
|
14
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
10
15
|
let {
|
|
11
16
|
appearance,
|
|
12
17
|
chipClassName,
|
|
@@ -54,11 +59,20 @@ let {
|
|
|
54
59
|
titleClassName,
|
|
55
60
|
titleFieldName = "name",
|
|
56
61
|
value = $bindable(),
|
|
62
|
+
checkboxIconPath = mdiCheckCircle,
|
|
63
|
+
uncheckboxIconPath = mdiCheckCircleOutline,
|
|
64
|
+
checkboxIconClassName = "",
|
|
65
|
+
uncheckboxIconClassName = "",
|
|
66
|
+
checkboxClassName = "",
|
|
67
|
+
itemTileSnippet,
|
|
57
68
|
...props
|
|
58
69
|
} = $props();
|
|
59
70
|
let searchFieldRef = $state(null);
|
|
60
71
|
let isPlaced = $state(false);
|
|
61
72
|
let searchText = $state("");
|
|
73
|
+
let selectedItemsSet = $state(
|
|
74
|
+
value ? new SvelteSet(Array.isArray(value) ? value : [value]) : new SvelteSet()
|
|
75
|
+
);
|
|
62
76
|
let comboboxIconSizeClassName = $derived.by(() => {
|
|
63
77
|
if (size) {
|
|
64
78
|
switch (size) {
|
|
@@ -120,17 +134,6 @@ let displayText = $derived.by(() => {
|
|
|
120
134
|
}
|
|
121
135
|
return "";
|
|
122
136
|
});
|
|
123
|
-
let selectedItemsSet = $derived.by(() => {
|
|
124
|
-
let set = /* @__PURE__ */ new Set();
|
|
125
|
-
if (value != null) {
|
|
126
|
-
if (Array.isArray(value)) {
|
|
127
|
-
value.forEach((v) => set.add(v));
|
|
128
|
-
} else if (value) {
|
|
129
|
-
set.add(value);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return set;
|
|
133
|
-
});
|
|
134
137
|
let preparedItems = $derived.by(() => {
|
|
135
138
|
return (items || []).map((item, index) => {
|
|
136
139
|
let res = {};
|
|
@@ -329,6 +332,7 @@ function handleItemClick(ev, item, index) {
|
|
|
329
332
|
{:else if filteredItems?.length}
|
|
330
333
|
<ul>
|
|
331
334
|
{#each filteredItems as item, index}
|
|
335
|
+
{@const isSelected = selectedItemsSet.has(item[identityFieldName])}
|
|
332
336
|
<li
|
|
333
337
|
class="select-none"
|
|
334
338
|
id="item-{index}"
|
|
@@ -336,16 +340,38 @@ function handleItemClick(ev, item, index) {
|
|
|
336
340
|
tabindex="-1"
|
|
337
341
|
aria-selected={item.isChecked}
|
|
338
342
|
>
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
343
|
+
{#if itemTileSnippet}
|
|
344
|
+
<ButtonListItem onClick={(ev) => handleItemClick(ev, item, index)}>
|
|
345
|
+
{@render itemTileSnippet(item, index)}
|
|
346
|
+
{#if hasCheckbox}
|
|
347
|
+
<div>
|
|
348
|
+
<Icon
|
|
349
|
+
path={isSelected ? checkboxIconPath : uncheckboxIconPath}
|
|
350
|
+
className="w-5 h-5 {checkboxClassName} {isSelected
|
|
351
|
+
? `text-primary ${checkboxIconClassName}`
|
|
352
|
+
: `text-base-400 ${uncheckboxIconClassName}`}"
|
|
353
|
+
/>
|
|
354
|
+
</div>
|
|
355
|
+
{/if}
|
|
356
|
+
</ButtonListItem>
|
|
357
|
+
{:else}
|
|
358
|
+
<ButtonListItem
|
|
359
|
+
title={item[titleFieldName] || ''}
|
|
360
|
+
subtitle={item[subtitleFieldName || ''] || ''}
|
|
361
|
+
{index}
|
|
362
|
+
{hasCheckbox}
|
|
363
|
+
className=" {itemClassName}"
|
|
364
|
+
titleClassName=" {titleClassName}"
|
|
365
|
+
subtitleClassName=" {subtitleClassName}"
|
|
366
|
+
isChecked={isSelected}
|
|
367
|
+
{checkboxIconPath}
|
|
368
|
+
{uncheckboxIconPath}
|
|
369
|
+
{checkboxIconClassName}
|
|
370
|
+
{uncheckboxIconClassName}
|
|
371
|
+
{checkboxClassName}
|
|
372
|
+
onClick={(ev) => handleItemClick(ev, item, index)}
|
|
373
|
+
/>
|
|
374
|
+
{/if}
|
|
349
375
|
</li>
|
|
350
376
|
{/each}
|
|
351
377
|
</ul>
|
|
@@ -38,6 +38,12 @@ export type ComboboxFieldProps = {
|
|
|
38
38
|
subtitleFieldName?: string;
|
|
39
39
|
titleClassName?: string;
|
|
40
40
|
titleFieldName?: string;
|
|
41
|
+
checkboxIconPath?: string;
|
|
42
|
+
uncheckboxIconPath?: string;
|
|
43
|
+
checkboxIconClassName?: string;
|
|
44
|
+
uncheckboxIconClassName?: string;
|
|
45
|
+
checkboxClassName?: string;
|
|
46
|
+
itemTileSnippet?: Snippet<[item: any, index: any]>;
|
|
41
47
|
};
|
|
42
48
|
import type { Snippet } from 'svelte';
|
|
43
49
|
import { type InputFieldProps } from '../input-field/input-field.svelte';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script module lang="ts"></script>
|
|
2
2
|
|
|
3
3
|
<script lang="ts">import { ripple } from "../../../../../actions";
|
|
4
|
+
import { getDialogSize, openPickerDialog } from "../../../../../services";
|
|
4
5
|
import EasyScriptLoader from "@cloudparker/easy-script-loader-svelte";
|
|
5
6
|
import InputField, {} from "../input-field/input-field.svelte";
|
|
6
|
-
import { isMobileScreen, openPickerDialog } from "../../../../../services";
|
|
7
7
|
let {
|
|
8
8
|
id,
|
|
9
9
|
name,
|
|
@@ -60,18 +60,15 @@ function formatDialCode(dialcode) {
|
|
|
60
60
|
}
|
|
61
61
|
return dialcode;
|
|
62
62
|
}
|
|
63
|
-
async function
|
|
63
|
+
async function handleDialCodePicker() {
|
|
64
64
|
if (EasyCountryData) {
|
|
65
65
|
let items = EasyCountryData.getCountries();
|
|
66
|
-
let size2 =
|
|
66
|
+
let size2 = getDialogSize();
|
|
67
67
|
let res = await openPickerDialog({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// hasCheck: true,
|
|
73
|
-
// identity: 'dialCode',
|
|
74
|
-
// search: ['name', 'dialCode', 'isoCode']
|
|
68
|
+
value: dialCode,
|
|
69
|
+
items,
|
|
70
|
+
identityFieldName: "dialCode",
|
|
71
|
+
itemTileSnippet: dialCodePickerItemTile
|
|
75
72
|
});
|
|
76
73
|
console.log(res);
|
|
77
74
|
if (res) {
|
|
@@ -118,13 +115,22 @@ function handleNumberKeyDown(ev) {
|
|
|
118
115
|
}
|
|
119
116
|
</script>
|
|
120
117
|
|
|
121
|
-
{#snippet
|
|
118
|
+
{#snippet dialCodePickerItemTile(item: any, index: number)}
|
|
119
|
+
<div class="w-14 text-sm font-bold text-base">
|
|
120
|
+
{item.dialCode}
|
|
121
|
+
</div>
|
|
122
|
+
<div class="flex-grow">
|
|
123
|
+
{item.name}
|
|
124
|
+
</div>
|
|
125
|
+
{/snippet}
|
|
126
|
+
|
|
127
|
+
{#snippet showDialCodeButton()}
|
|
122
128
|
<button
|
|
123
129
|
id="btn-dialcode-picker-{name || id}"
|
|
124
130
|
type="button"
|
|
125
131
|
class="w-16 h-full hover:bg-gray-100 font-bold text-gray-400 focus:outline-primary {btnRoundedClassName} {buttonClassName}"
|
|
126
132
|
use:ripple
|
|
127
|
-
onclick={
|
|
133
|
+
onclick={handleDialCodePicker}
|
|
128
134
|
>
|
|
129
135
|
{_dailCode}
|
|
130
136
|
</button>
|
|
@@ -150,7 +156,7 @@ function handleNumberKeyDown(ev) {
|
|
|
150
156
|
{id}
|
|
151
157
|
{name}
|
|
152
158
|
maxlength={props?.maxlength || 12}
|
|
153
|
-
leftSnippet={
|
|
159
|
+
leftSnippet={showDialCodeButton}
|
|
154
160
|
{size}
|
|
155
161
|
{appearance}
|
|
156
162
|
{floatingLabel}
|