Upgrade to vue3 (#16)

* upgrade to vue 3.x and framework7 8.x
* change calendar plugin to vue-datepicker
* disable export button when user does not hava any transaction
* implement new pin code input
* append thousands separator in amount in exchange rates page
This commit is contained in:
mayswind
2023-04-21 01:45:00 +08:00
committed by GitHub
parent 4b0f7d45e8
commit b1c765eb51
89 changed files with 8353 additions and 16671 deletions
+21 -17
View File
@@ -1,27 +1,27 @@
<template>
<f7-sheet :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
:opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left"></div>
<div class="right">
<f7-link sheet-close :text="$t('Done')"></f7-link>
</div>
</f7-toolbar>
<f7-page-content>
<f7-block class="margin-vertical">
<f7-row class="padding-vertical padding-horizontal-half"
:class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
v-for="(row, idx) in allColorRows" :key="idx">
<f7-col class="text-align-center" v-for="colorInfo in row" :key="colorInfo.color">
<f7-icon f7="app_fill"
:style="colorInfo.color | iconStyle('default', 'var(--default-icon-color)')"
@click.native="onColorClicked(colorInfo)">
<f7-block class="margin-vertical no-padding">
<div class="grid grid-cols-7 padding-vertical-half padding-horizontal-half"
:class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
:key="idx" v-for="(row, idx) in allColorRows">
<div class="text-align-center" :key="colorInfo.color" v-for="colorInfo in row">
<ItemIcon icon-type="fixed-f7" icon-id="app_fill" :color="colorInfo.color" @click="onColorClicked(colorInfo)">
<f7-badge color="default" class="right-bottom-icon" v-if="currentValue && currentValue === colorInfo.color">
<f7-icon f7="checkmark_alt"></f7-icon>
</f7-badge>
</f7-icon>
</f7-col>
<f7-col v-for="idx in (itemPerRow - row.length)" :key="idx"></f7-col>
</f7-row>
</ItemIcon>
</div>
</div>
</f7-block>
</f7-page-content>
</f7-sheet>
@@ -30,16 +30,20 @@
<script>
export default {
props: [
'value',
'modelValue',
'columnCount',
'show',
'allColorInfos'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
currentValue: self.value,
currentValue: self.modelValue,
itemPerRow: self.columnCount || 7
}
},
@@ -64,11 +68,11 @@ export default {
methods: {
onColorClicked(colorInfo) {
this.currentValue = colorInfo.color;
this.$emit('input', this.currentValue);
this.$emit('update:modelValue', this.currentValue);
this.$emit('update:show', false);
},
onSheetOpen(event) {
this.currentValue = this.value;
this.currentValue = this.modelValue;
this.scrollToSelectedItem(event.$el);
},
onSheetClosed() {
@@ -1,56 +1,39 @@
<template>
<f7-sheet style="height:auto" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" style="height:auto"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<div class="swipe-handler"></div>
<f7-page-content>
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px" v-if="title"><b>{{ title }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin-top margin-bottom-half" v-if="hint">{{ hint }}</p>
<p class="no-margin-top" v-if="hint">{{ hint }}</p>
<p class="no-margin-top margin-bottom" v-if="beginDateTime && endDateTime">
<span>{{ beginDateTime }}</span>
<span> - </span>
<span>{{ endDateTime }}</span>
</p>
<slot></slot>
<f7-list no-hairlines inline-labels class="no-margin-top margin-bottom">
<f7-list-input
:label="$t('Begin Time')"
type="datepicker"
class="date-range-sheet-time-item"
:calendar-params="{
timePicker: true,
dateFormat: $t('input-format.datetime.long'),
firstDay: defaultFirstDayOfWeek,
toolbarCloseText: $t('Done'),
timePickerPlaceholder: $t('Select Time'),
timePickerFormat: $locale.getInputTimeIntlDateTimeFormatOptions(),
monthNames: $locale.getAllLongMonthNames(),
monthNamesShort: $locale.getAllShortMonthNames(),
dayNames: $locale.getAllLongWeekdayNames(),
dayNamesShort: $locale.getAllShortWeekdayNames()}"
:value="currentMinDate"
@calendar:change="currentMinDate = $event"
>
</f7-list-input>
<f7-list-input
:label="$t('End Time')"
type="datepicker"
class="date-range-sheet-time-item"
:calendar-params="{
timePicker: true,
dateFormat: $t('input-format.datetime.long'),
firstDay: defaultFirstDayOfWeek,
toolbarCloseText: $t('Done'),
timePickerPlaceholder: $t('Select Time'),
timePickerFormat: $locale.getInputTimeIntlDateTimeFormatOptions(),
monthNames: $locale.getAllLongMonthNames(),
monthNamesShort: $locale.getAllShortMonthNames(),
dayNames: $locale.getAllLongWeekdayNames(),
dayNamesShort: $locale.getAllShortWeekdayNames()}"
:value="currentMaxDate"
@calendar:change="currentMaxDate = $event"
>
</f7-list-input>
</f7-list>
<VueDatePicker range inline enable-seconds six-weeks
auto-apply month-name-format="long"
class="margin-bottom"
:dark="isDarkMode"
:week-start="firstDayOfWeek"
:year-range="yearRange"
:day-names="dayNames"
:is24="is24Hour"
:partial-range="false"
:preset-ranges="presetRanges"
v-model="dateRange">
<template #month="{ text }">
{{ $t(`datetime.${text}.short`) }}
</template>
<template #month-overlay-value="{ text }">
{{ $t(`datetime.${text}.short`) }}
</template>
</VueDatePicker>
<f7-button large fill
:class="{ 'disabled': !currentMinDate || !currentMaxDate }"
:class="{ 'disabled': !dateRange[0] || !dateRange[1] }"
:text="$t('Continue')"
@click="confirm">
</f7-button>
@@ -71,6 +54,10 @@ export default {
'hint',
'show'
],
emits: [
'update:show',
'dateRange:change'
],
data() {
const self = this;
let minDate = self.$utilities.getTodayFirstUnixTime();
@@ -84,65 +71,87 @@ export default {
maxDate = self.maxTime;
}
minDate = self.$utilities.getDummyUnixTimeForLocalUsage(minDate, self.$utilities.getTimezoneOffsetMinutes(), self.$utilities.getBrowserTimezoneOffsetMinutes());
maxDate = self.$utilities.getDummyUnixTimeForLocalUsage(maxDate, self.$utilities.getTimezoneOffsetMinutes(), self.$utilities.getBrowserTimezoneOffsetMinutes());
return {
currentMinDate: [self.$utilities.getLocalDatetimeFromUnixTime(minDate)],
currentMaxDate: [self.$utilities.getLocalDatetimeFromUnixTime(maxDate)]
yearRange: [
2000,
this.$utilities.getYear(this.$utilities.getCurrentDateTime()) + 1
],
dateRange: [
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(minDate, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes())),
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(maxDate, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()))
]
}
},
computed: {
defaultFirstDayOfWeek() {
return this.$store.getters.currentUserFirstDayOfWeek;
}
},
watch: {
'currentMinDate': function (newValue) {
if (!newValue) {
this.currentMinDate = [this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getCurrentUnixTime())];
}
isDarkMode() {
return this.$root.isDarkMode;
},
'currentMaxDate': function (newValue) {
if (!newValue) {
this.currentMaxDate = [this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getCurrentUnixTime())];
}
firstDayOfWeek() {
return this.$store.getters.currentUserFirstDayOfWeek;
},
dayNames() {
return this.$locale.getAllMinWeekdayNames();
},
is24Hour() {
const datetimeFormat = this.$t('format.datetime.long');
return this.$utilities.is24HourFormat(datetimeFormat);
},
beginDateTime() {
const actualBeginUnixTime = this.$utilities.getActualUnixTimeForStore(this.$utilities.getUnixTime(this.dateRange[0]), this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
return this.$utilities.formatUnixTime(actualBeginUnixTime, this.$t('format.datetime.long'));
},
endDateTime() {
const actualEndUnixTime = this.$utilities.getActualUnixTimeForStore(this.$utilities.getUnixTime(this.dateRange[1]), this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
return this.$utilities.formatUnixTime(actualEndUnixTime, this.$t('format.datetime.long'));
},
presetRanges() {
const presetRanges = [];
[
this.$constants.datetime.allDateRanges.Today,
this.$constants.datetime.allDateRanges.LastSevenDays,
this.$constants.datetime.allDateRanges.LastThirtyDays,
this.$constants.datetime.allDateRanges.ThisWeek,
this.$constants.datetime.allDateRanges.ThisMonth,
this.$constants.datetime.allDateRanges.ThisYear
].forEach(dateRangeType => {
const dateRange = this.$utilities.getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
presetRanges.push({
label: this.$t(dateRangeType.name),
range: [
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(dateRange.minTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes())),
this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(dateRange.maxTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()))
]
});
});
return presetRanges;
}
},
methods: {
onSheetOpen() {
if (this.minTime) {
const minTime = this.$utilities.getDummyUnixTimeForLocalUsage(this.minTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
this.currentMinDate = [this.$utilities.getLocalDatetimeFromUnixTime(minTime)];
this.dateRange[0] = this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(this.minTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()));
}
if (this.maxTime) {
const maxTime = this.$utilities.getDummyUnixTimeForLocalUsage(this.maxTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes());
this.currentMaxDate = [this.$utilities.getLocalDatetimeFromUnixTime(maxTime)];
this.dateRange[1] = this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getDummyUnixTimeForLocalUsage(this.maxTime, this.$utilities.getTimezoneOffsetMinutes(), this.$utilities.getBrowserTimezoneOffsetMinutes()));
}
},
onSheetClosed() {
this.$emit('update:show', false);
},
confirm() {
if (!this.currentMinDate || !this.currentMaxDate) {
if (!this.dateRange[0] || !this.dateRange[1]) {
return;
}
let currentMinDate = this.currentMinDate;
const currentMinDate = this.dateRange[0];
const currentMaxDate = this.dateRange[1];
if (this.$utilities.isArray(this.currentMinDate)) {
currentMinDate = this.currentMinDate[0];
}
let currentMaxDate = this.currentMaxDate;
if (this.$utilities.isArray(this.currentMaxDate)) {
currentMaxDate = this.currentMaxDate[0];
}
let minUnixTime = this.$utilities.getMinuteFirstUnixTime(currentMinDate);
let maxUnixTime = this.$utilities.getMinuteLastUnixTime(currentMaxDate);
let minUnixTime = this.$utilities.getUnixTime(currentMinDate);
let maxUnixTime = this.$utilities.getUnixTime(currentMaxDate);
if (minUnixTime < 0 || maxUnixTime < 0) {
this.$toast('Date is too early');
@@ -160,9 +169,3 @@ export default {
}
}
</script>
<style>
.list .date-range-sheet-time-item > .item-content {
padding-left: 0;
}
</style>
@@ -0,0 +1,110 @@
<template>
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" class="date-time-selection-sheet" style="height:auto"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left">
<f7-link :text="$t('Current Time')" @click="setCurrentTime"></f7-link>
</div>
<div class="right">
<f7-link :text="$t('Done')" @click="confirm"></f7-link>
</div>
</f7-toolbar>
<f7-page-content>
<VueDatePicker inline enable-seconds
auto-apply month-name-format="long"
class="justify-content-center"
:dark="isDarkMode"
:week-start="firstDayOfWeek"
:year-range="yearRange"
:day-names="dayNames"
:is24="is24Hour"
v-model="dateTime">
<template #month="{ text }">
{{ $t(`datetime.${text}.short`) }}
</template>
<template #month-overlay-value="{ text }">
{{ $t(`datetime.${text}.short`) }}
</template>
</VueDatePicker>
</f7-page-content>
</f7-sheet>
</template>
<script>
export default {
props: [
'modelValue',
'show'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
let value = self.$utilities.getCurrentUnixTime();
if (self.modelValue) {
value = self.modelValue;
}
return {
yearRange: [
2000,
this.$utilities.getYear(this.$utilities.getCurrentDateTime()) + 1
],
dateTime: this.$utilities.getLocalDatetimeFromUnixTime(value),
}
},
computed: {
isDarkMode() {
return this.$root.isDarkMode;
},
firstDayOfWeek() {
return this.$store.getters.currentUserFirstDayOfWeek;
},
dayNames() {
return this.$locale.getAllMinWeekdayNames();
},
is24Hour() {
const datetimeFormat = this.$t('format.datetime.long');
return this.$utilities.is24HourFormat(datetimeFormat);
}
},
methods: {
onSheetOpen() {
if (this.modelValue) {
this.dateTime = this.$utilities.getLocalDatetimeFromUnixTime(this.modelValue)
}
},
onSheetClosed() {
this.$emit('update:show', false);
},
setCurrentTime() {
this.dateTime = this.$utilities.getLocalDatetimeFromUnixTime(this.$utilities.getCurrentUnixTime())
},
confirm() {
if (!this.dateTime) {
return;
}
const unixTime = this.$utilities.getUnixTime(this.dateTime);
if (unixTime < 0) {
this.$toast('Date is too early');
return;
}
this.$emit('update:modelValue', unixTime);
this.$emit('update:show', false);
}
}
}
</script>
<style>
.date-time-selection-sheet .dp__menu {
border: 0;
}
</style>
+33 -19
View File
@@ -1,27 +1,27 @@
<template>
<f7-sheet :class="{ 'icon-selection-huge-sheet': hugeIconRows }" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
:class="heightClass" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left"></div>
<div class="right">
<f7-link sheet-close :text="$t('Done')"></f7-link>
</div>
</f7-toolbar>
<f7-page-content>
<f7-block class="margin-vertical">
<f7-row class="padding-vertical-half padding-horizontal-half"
:class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
v-for="(row, idx) in allIconRows" :key="idx">
<f7-col class="text-align-center" v-for="iconInfo in row" :key="iconInfo.id">
<f7-icon :icon="iconInfo.icon"
:style="color | iconStyle('default', 'var(--default-icon-color)')"
@click.native="onIconClicked(iconInfo)">
<f7-block class="margin-vertical no-padding">
<div class="grid grid-cols-7 padding-vertical-half padding-horizontal-half"
:class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
:key="idx" v-for="(row, idx) in allIconRows">
<div class="text-align-center" :key="iconInfo.id" v-for="iconInfo in row">
<ItemIcon icon-type="fixed" :icon-id="iconInfo.icon" :color="color" @click="onIconClicked(iconInfo)">
<f7-badge color="default" class="right-bottom-icon" v-if="currentValue && currentValue === iconInfo.id">
<f7-icon f7="checkmark_alt"></f7-icon>
</f7-badge>
</f7-icon>
</f7-col>
<f7-col v-for="idx in (itemPerRow - row.length)" :key="idx"></f7-col>
</f7-row>
</ItemIcon>
</div>
</div>
</f7-block>
</f7-page-content>
</f7-sheet>
@@ -30,17 +30,21 @@
<script>
export default {
props: [
'value',
'modelValue',
'color',
'columnCount',
'show',
'allIconInfos'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
currentValue: self.value,
currentValue: self.modelValue,
itemPerRow: self.columnCount || 7
}
},
@@ -71,18 +75,24 @@ export default {
return ret;
},
hugeIconRows() {
return this.allIconRows.length > 10;
heightClass() {
if (this.allIconRows.length > 10) {
return 'icon-selection-huge-sheet';
} else if (this.allIconRows.length > 6) {
return 'icon-selection-large-sheet';
} else {
return '';
}
}
},
methods: {
onIconClicked(iconInfo) {
this.currentValue = iconInfo.id;
this.$emit('input', this.currentValue);
this.$emit('update:modelValue', this.currentValue);
this.$emit('update:show', false);
},
onSheetOpen(event) {
this.currentValue = this.value;
this.currentValue = this.modelValue;
this.scrollToSelectedItem(event.$el);
},
onSheetClosed() {
@@ -128,6 +138,10 @@ export default {
<style>
@media (min-height: 630px) {
.icon-selection-large-sheet {
height: 310px;
}
.icon-selection-huge-sheet {
height: 400px;
}
+51 -9
View File
@@ -1,18 +1,21 @@
<template>
<f7-sheet style="height:auto" :opened="show" @sheet:closed="onSheetClosed">
<f7-page-content>
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" style="height:auto"
:opened="show" @sheet:closed="onSheetClosed">
<div class="swipe-handler"></div>
<f7-page-content class="margin-top no-padding-top">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px" v-if="title"><b>{{ title }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin-top margin-bottom-half" v-if="hint">
<span>{{ hint }}</span>
<f7-link class="icon-after-text"
<f7-link id="copy-to-clipboard-icon" ref="copyToClipboardIcon"
class="icon-after-text"
icon-only icon-f7="doc_on_doc" icon-size="16px"
v-if="enableCopy"
v-clipboard:copy="information" v-clipboard:success="onCopied"></f7-link>
></f7-link>
</p>
<textarea class="information-content full-line" :rows="rowCount" readonly="readonly" v-model="information"></textarea>
<textarea class="information-content full-line" :rows="rowCount" :value="information"></textarea>
<div class="margin-top text-align-center">
<f7-link @click="cancel" :text="$t('Close')"></f7-link>
</div>
@@ -31,14 +34,53 @@ export default {
'enableCopy',
'show'
],
emits: [
'update:show',
'info:copied'
],
data() {
return {
clipboardHolder: null
}
},
mounted() {
this.makeCopyToClipboardClickable();
},
updated() {
this.makeCopyToClipboardClickable();
},
watch: {
'information': function (newValue) {
if (this.clipboardHolder) {
this.$utilities.changeClipboardObjectText(this.clipboardHolder, newValue);
}
}
},
methods: {
onSheetClosed() {
this.$emit('update:show', false);
},
onCopied() {
this.$emit('info:copied');
this.close();
},
cancel() {
this.close();
},
makeCopyToClipboardClickable() {
const self = this;
if (self.clipboardHolder) {
return;
}
if (self.$refs.copyToClipboardIcon) {
self.clipboardHolder = self.$utilities.makeButtonCopyToClipboard({
el: '#copy-to-clipboard-icon',
text: self.information,
successCallback: function () {
self.$emit('info:copied');
}
});
}
},
close() {
this.$emit('update:show', false);
}
}
+127
View File
@@ -0,0 +1,127 @@
<template>
<f7-icon :f7="f7Icon" :icon="icon" :style="style">
<slot></slot>
</f7-icon>
</template>
<script>
export default {
props: [
'iconType',
'iconId',
'color',
'defaultColor',
'additionalColorAttr'
],
computed: {
f7Icon() {
if (this.iconType === 'fixed-f7') {
return this.iconId;
} else {
return '';
}
},
icon() {
if (this.iconType === 'account') {
return this.getAccountIcon(this.iconId);
} else if (this.iconType === 'category') {
return this.getCategoryIcon(this.iconId);
} else if (this.iconType === 'fixed') {
return this.iconId;
} else {
return '';
}
},
style() {
let defaultColor = 'var(--default-icon-color)';
if (this.defaultColor) {
defaultColor = this.defaultColor;
}
if (this.iconType === 'account') {
return this.getAccountIconStyle(this.color, defaultColor, this.additionalColorAttr);
} else if (this.iconType === 'category') {
return this.getCategoryIconStyle(this.color, defaultColor, this.additionalColorAttr);
} else {
return this.getDefaultIconStyle(this.color, defaultColor, this.additionalColorAttr);
}
}
},
methods: {
getAccountIcon(iconId) {
if (this.$utilities.isNumber(iconId)) {
iconId = iconId.toString();
}
if (!this.$constants.icons.allAccountIcons[iconId]) {
return this.$constants.icons.defaultAccountIcon.icon;
}
return this.$constants.icons.allAccountIcons[iconId].icon;
},
getCategoryIcon(iconId) {
if (this.$utilities.isNumber(iconId)) {
iconId = iconId.toString();
}
if (!this.$constants.icons.allCategoryIcons[iconId]) {
return this.$constants.icons.defaultCategoryIcon.icon;
}
return this.$constants.icons.allCategoryIcons[iconId].icon;
},
getAccountIconStyle(color, defaultColor, additionalColorAttr) {
if (color && color !== this.$constants.colors.defaultAccountColor) {
color = '#' + color;
} else {
color = defaultColor;
}
const ret = {
color: color
};
if (additionalColorAttr) {
ret[additionalColorAttr] = color;
}
return ret;
},
getCategoryIconStyle(color, defaultColor, additionalColorAttr) {
if (color && color !== this.$constants.colors.defaultCategoryColor) {
color = '#' + color;
} else {
color = defaultColor;
}
const ret = {
color: color
};
if (additionalColorAttr) {
ret[additionalColorAttr] = color;
}
return ret;
},
getDefaultIconStyle(color, defaultColor, additionalColorAttr) {
if (color && color !== this.$constants.colors.defaultColor) {
color = '#' + color;
} else {
color = defaultColor;
}
const ret = {
color: color
};
if (additionalColorAttr) {
ret[additionalColorAttr] = color;
}
return ret;
}
}
}
</script>
@@ -1,25 +1,29 @@
<template>
<f7-sheet :class="{ 'list-item-selection-huge-sheet': hugeListItemRows }" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
:class="{ 'list-item-selection-huge-sheet': hugeListItemRows }" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left"></div>
<div class="right">
<f7-link sheet-close :text="$t('Done')"></f7-link>
</div>
</f7-toolbar>
<f7-page-content>
<f7-list no-hairlines class="no-margin-top no-margin-bottom">
<f7-list dividers no-hairlines class="no-margin-vertical">
<f7-list-item link="#" no-chevron
v-for="(item, index) in items"
:key="item | itemKeyValue(index, keyField, valueType)"
:title="$tIf((titleField ? item[titleField] : item), titleI18n)"
:value="getItemValue(item, index, valueField, valueType)"
:class="{ 'list-item-selected': isSelected(item, index) }"
:value="item | itemKeyValue(index, valueField, valueType)"
:title="item | itemFieldContent(titleField, item, titleI18n)"
:key="getItemValue(item, index, keyField, valueType)"
v-for="(item, index) in items"
@click="onItemClicked(item, index)">
<f7-icon slot="media"
:icon="item[iconField] | icon(iconType)"
:style="item[colorField] | iconStyle(iconType, 'var(--default-icon-color)')"
v-if="iconField"></f7-icon>
<f7-icon slot="after" class="list-item-checked-icon" f7="checkmark_alt" v-if="isSelected(item, index)"></f7-icon>
<template #content-start>
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" :style="{ 'color': isSelected(item, index) ? '' : 'transparent' }"></f7-icon>
</template>
<template #media v-if="iconField">
<ItemIcon :icon-type="iconType" :icon-id="item[iconField]" :color="item[colorField]"></ItemIcon>
</template>
</f7-list-item>
</f7-list>
</f7-page-content>
@@ -29,7 +33,7 @@
<script>
export default {
props: [
'value',
'modelValue',
'valueType', // item or index
'keyField', // for value type == item
'valueField', // for value type == item
@@ -41,11 +45,15 @@ export default {
'items',
'show'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
currentValue: self.value
currentValue: self.modelValue
}
},
computed: {
@@ -54,6 +62,15 @@ export default {
}
},
methods: {
getItemValue(item, index, fieldName, valueType) {
if (valueType === 'index') {
return index;
} else if (fieldName) {
return item[fieldName];
} else {
return item;
}
},
onItemClicked(item, index) {
if (this.valueType === 'index') {
this.currentValue = index;
@@ -65,15 +82,15 @@ export default {
}
}
this.$emit('input', this.currentValue);
this.$emit('update:show', false);
this.$emit('update:modelValue', this.currentValue);
this.close();
},
onSheetOpen(event) {
this.currentValue = this.value;
this.currentValue = this.modelValue;
this.scrollToSelectedItem(event.$el);
},
onSheetClosed() {
this.$emit('update:show', false);
this.close();
},
isSelected(item, index) {
if (this.valueType === 'index') {
@@ -106,17 +123,9 @@ export default {
}
container.scrollTop(targetPos);
}
},
filters: {
itemKeyValue(item, index, fieldName, valueType) {
if (valueType === 'index') {
return index;
} else if (fieldName) {
return item[fieldName];
} else {
return item;
}
},
close() {
this.$emit('update:show', false);
}
}
}
+38 -30
View File
@@ -1,53 +1,55 @@
<template>
<f7-sheet class="numpad-sheet" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-page-content class="no-margin no-padding-top">
<f7-row class="numpad-values">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" class="numpad-sheet" style="height: auto"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<div class="swipe-handler"></div>
<f7-page-content class="margin-top no-padding-top">
<div class="numpad-values">
<span class="numpad-value" :style="{ fontSize: currentDisplayFontSize + 'px' }">{{ currentDisplay }}</span>
</f7-row>
<f7-row class="numpad-buttons">
<f7-button class="numpad-button numpad-button-num" @click="inputNum(7)">
</div>
<div class="numpad-buttons">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(7)">
<span class="numpad-button-text numpad-button-text-normal">7</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(8)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(8)">
<span class="numpad-button-text numpad-button-text-normal">8</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(9)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(9)">
<span class="numpad-button-text numpad-button-text-normal">9</span>
</f7-button>
<f7-button class="numpad-button numpad-button-function no-right-border" @click="setSymbol('×')">
<f7-button class="numpad-button numpad-button-function no-right-border" @mousedown="setSymbol('×')">
<span class="numpad-button-text numpad-button-text-normal">&times;</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(4)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(4)">
<span class="numpad-button-text numpad-button-text-normal">4</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(5)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(5)">
<span class="numpad-button-text numpad-button-text-normal">5</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(6)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(6)">
<span class="numpad-button-text numpad-button-text-normal">6</span>
</f7-button>
<f7-button class="numpad-button numpad-button-function no-right-border" @click="setSymbol('')">
<f7-button class="numpad-button numpad-button-function no-right-border" @mousedown="setSymbol('')">
<span class="numpad-button-text numpad-button-text-normal">&minus;</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(1)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(1)">
<span class="numpad-button-text numpad-button-text-normal">1</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(2)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(2)">
<span class="numpad-button-text numpad-button-text-normal">2</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(3)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(3)">
<span class="numpad-button-text numpad-button-text-normal">3</span>
</f7-button>
<f7-button class="numpad-button numpad-button-function no-right-border" @click="setSymbol('+')">
<f7-button class="numpad-button numpad-button-function no-right-border" @mousedown="setSymbol('+')">
<span class="numpad-button-text numpad-button-text-normal">&plus;</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputDot()">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputDot()">
<span class="numpad-button-text numpad-button-text-normal">.</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(0)">
<f7-button class="numpad-button numpad-button-num" @mousedown="inputNum(0)">
<span class="numpad-button-text numpad-button-text-normal">0</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="backspace" @taphold.native="clear()">
<f7-button class="numpad-button numpad-button-num" @mousedown="backspace" @taphold="clear()">
<span class="numpad-button-text numpad-button-text-normal">
<f7-icon f7="delete_left"></f7-icon>
</span>
@@ -55,7 +57,7 @@
<f7-button class="numpad-button numpad-button-confirm no-right-border no-bottom-border" fill @click="confirm()">
<span :class="{ 'numpad-button-text': true, 'numpad-button-text-confirm': !currentSymbol }">{{ confirmText }}</span>
</f7-button>
</f7-row>
</div>
</f7-page-content>
</f7-sheet>
</template>
@@ -63,18 +65,22 @@
<script>
export default {
props: [
'value',
'modelValue',
'minValue',
'maxValue',
'show'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
previousValue: '',
currentSymbol: '',
currentValue: self.getStringValue(self.value)
currentValue: self.getStringValue(self.modelValue)
}
},
computed: {
@@ -103,7 +109,7 @@ export default {
if (this.currentSymbol) {
return '=';
} else {
return this.$i18n.t('OK');
return this.$t('OK');
}
}
},
@@ -291,17 +297,20 @@ export default {
} else {
const value = this.$utilities.stringCurrencyToNumeric(this.currentValue);
this.$emit('input', value);
this.$emit('update:show', false);
this.$emit('update:modelValue', value);
this.close();
return true;
}
},
close() {
this.$emit('update:show', false);
},
onSheetOpen() {
this.currentValue = this.getStringValue(this.value);
this.currentValue = this.getStringValue(this.modelValue);
},
onSheetClosed() {
this.$emit('update:show', false);
this.close();
}
}
}
@@ -322,7 +331,6 @@ export default {
padding-left: 16px;
line-height: 1;
height: 50px;
justify-content: center;
align-items: center;
box-sizing: border-box;
user-select: none;
@@ -371,7 +379,7 @@ export default {
color: var(--f7-color-black);
}
.theme-dark .numpad-button-text-normal {
.dark .numpad-button-text-normal {
color: var(--f7-color-white);
}
+22 -11
View File
@@ -1,23 +1,26 @@
<template>
<f7-sheet style="height:auto" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-page-content>
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" style="height:auto"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<div class="swipe-handler"></div>
<f7-page-content class="margin-top no-padding-top">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px" v-if="title"><b>{{ title }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin-top margin-bottom-half" v-if="hint">{{ hint }}</p>
<p class="no-margin" v-if="hint">{{ hint }}</p>
<slot></slot>
<f7-list no-hairlines class="no-margin-top margin-bottom">
<f7-list no-hairlines strong class="no-margin">
<f7-list-input
type="number"
autocomplete="one-time-code"
outline
floating-label
clear-button
class="no-margin no-padding-bottom"
:label="$t('Password')"
:placeholder="$t('Passcode')"
:value="currentPasscode"
@input="currentPasscode = $event.target.value"
@keyup.enter.native="confirm()"
v-model:value="currentPasscode"
@keyup.enter="confirm()"
></f7-list-input>
</f7-list>
<f7-button large fill
@@ -36,13 +39,18 @@
<script>
export default {
props: [
'value',
'modelValue',
'title',
'hint',
'confirmDisabled',
'cancelDisabled',
'show'
],
emits: [
'update:modelValue',
'update:show',
'passcode:confirm'
],
data() {
return {
currentPasscode: ''
@@ -53,17 +61,20 @@ export default {
this.currentPasscode = '';
},
onSheetClosed() {
this.$emit('update:show', false);
this.close();
},
confirm() {
if (!this.currentPasscode || this.confirmDisabled) {
return;
}
this.$emit('input', this.currentPasscode);
this.$emit('update:modelValue', this.currentPasscode);
this.$emit('passcode:confirm', this.currentPasscode);
},
cancel() {
this.close();
},
close() {
this.$emit('update:show', false);
}
}
+22 -11
View File
@@ -1,22 +1,25 @@
<template>
<f7-sheet style="height:auto" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-page-content>
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" style="height:auto"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<div class="swipe-handler"></div>
<f7-page-content class="margin-top no-padding-top">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px" v-if="title"><b>{{ title }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin-top margin-bottom-half" v-if="hint">{{ hint }}</p>
<f7-list no-hairlines class="no-margin-top margin-bottom">
<p class="no-margin" v-if="hint">{{ hint }}</p>
<f7-list no-hairlines strong class="no-margin">
<f7-list-input
type="password"
autocomplete="current-password"
outline
floating-label
clear-button
class="no-margin no-padding-bottom"
:label="$t('Password')"
:placeholder="$t('Password')"
:value="currentPassword"
@input="currentPassword = $event.target.value"
@keyup.enter.native="confirm()"
v-model:value="currentPassword"
@keyup.enter="confirm()"
></f7-list-input>
</f7-list>
<f7-button large fill
@@ -35,13 +38,18 @@
<script>
export default {
props: [
'value',
'modelValue',
'title',
'hint',
'confirmDisabled',
'cancelDisabled',
'show'
],
emits: [
'update:modelValue',
'update:show',
'password:confirm'
],
data() {
return {
currentPassword: ''
@@ -52,17 +60,20 @@ export default {
this.currentPassword = '';
},
onSheetClosed() {
this.$emit('update:show', false);
this.close();
},
confirm() {
if (!this.currentPassword || this.confirmDisabled) {
return;
}
this.$emit('input', this.currentPassword);
this.$emit('update:modelValue', this.currentPassword);
this.$emit('password:confirm', this.currentPassword);
},
cancel() {
this.close();
},
close() {
this.$emit('update:show', false);
}
}
+46 -21
View File
@@ -4,14 +4,15 @@
<circle class="pie-chart-background" cx="0" cy="0" :r="diameter"></circle>
<circle class="pie-chart-item"
v-for="(item, idx) in validItems" :key="idx"
fill="transparent"
cx="0" cy="0"
:r="diameter / 2"
:stroke="item.color | defaultIconColor('var(--default-icon-color)')"
:stroke="getColor(item.color)"
:stroke-width="diameter"
:stroke-dasharray="item | itemStrokeDash(circumference)"
:stroke-dashoffset="item | itemDashOffset(validItems, circumference, itemCommonDashOffset)"
:stroke-dasharray="getItemStrokeDash(item)"
:stroke-dashoffset="getItemDashOffset(item, validItems, itemCommonDashOffset)"
:key="idx"
v-for="(item, idx) in validItems"
@click="switchSelectedIndex(idx)">
</circle>
@@ -48,8 +49,8 @@
<span class="skeleton-text">Percent</span>
</f7-chip>
<f7-chip outline
:text="(selectedItem.percent) | percent(2, '&lt;0.01')"
:style="(selectedItem ? selectedItem.color : '') | iconStyle('default', 'var(--default-icon-color)', '--f7-chip-outline-border-color')"
:text="$utilities.formatPercent(selectedItem.percent, 2, '&lt;0.01')"
:style="getColorStyle(selectedItem ? selectedItem.color : '', '--f7-chip-outline-border-color')"
v-else-if="!skeleton"></f7-chip>
</p>
<p v-else-if="!validItems || !validItems.length">
@@ -59,7 +60,7 @@
<span class="skeleton-text" v-if="skeleton">Name</span>
<span v-else-if="!skeleton && selectedItem.name">{{ selectedItem.name }}</span>
<span class="skeleton-text" v-if="skeleton">Value</span>
<span v-else-if="!skeleton && showValue" :style="(selectedItem ? selectedItem.color : '') | iconStyle('default', 'var(--default-icon-color)')">{{ selectedItem.value | currency(selectedItem.currency || defaultCurrency) }}</span>
<span v-else-if="!skeleton && showValue" :style="getColorStyle(selectedItem ? selectedItem.color : '')">{{ $locale.getDisplayCurrency(selectedItem.value, (selectedItem.currency || defaultCurrency)) }}</span>
<f7-icon class="item-navigate-icon" f7="chevron_right" v-if="enableClickItem"></f7-icon>
</f7-link>
<f7-link :no-link-class="true" v-else-if="!validItems || !validItems.length">
@@ -107,6 +108,9 @@ export default {
'enableClickItem',
'centerTextBackground',
],
emits: [
'click'
],
data: function () {
const diameter = 100;
@@ -195,8 +199,11 @@ export default {
}
},
watch: {
'items': function () {
this.selectedIndex = 0;
'items': {
handler() {
this.selectedIndex = 0;
},
deep: true
}
},
methods: {
@@ -216,14 +223,32 @@ export default {
if (this.enableClickItem) {
this.$emit('click', item.sourceItem);
}
}
},
filters: {
itemStrokeDash(item, circumference) {
const length = item.actualPercent * circumference;
return `${length} ${circumference - length}`;
},
itemDashOffset(item, items, circumference, offset) {
getColor: function (color) {
if (color && color !== this.$constants.colors.defaultColor) {
color = '#' + color;
} else {
color = 'var(--default-icon-color)';
}
return color;
},
getColorStyle: function (color, additionalFieldName) {
const ret = {
color: this.getColor(color)
};
if (additionalFieldName) {
ret[additionalFieldName] = ret.color;
}
return ret;
},
getItemStrokeDash(item) {
const length = item.actualPercent * this.circumference;
return `${length} ${this.circumference - length}`;
},
getItemDashOffset(item, items, offset) {
let allPreviousPercent = 0;
for (let i = 0; i < items.length; i++) {
@@ -237,17 +262,17 @@ export default {
}
if (offset) {
offset += circumference / 4;
offset += this.circumference / 4;
} else {
offset = circumference / 4;
offset = this.circumference / 4;
}
if (allPreviousPercent <= 0) {
return offset;
}
const allPreviousLength = allPreviousPercent * circumference;
return circumference - allPreviousLength + offset;
const allPreviousLength = allPreviousPercent * this.circumference;
return this.circumference - allPreviousLength + offset;
}
}
}
@@ -314,7 +339,7 @@ export default {
fill: #f0f0f0;
}
.theme-dark .pie-chart-background {
.dark .pie-chart-background {
fill: #181818;
}
+248
View File
@@ -0,0 +1,248 @@
<template>
<div class="pin-code-input grid grid-gap" :class="'grid-cols-' + length">
<div class="input input-outline input-with-value"
:key="index" v-for="(code, index) in codes">
<input min="0" maxlength="1" pattern="[0-9]*"
:ref="`pin-code-input-${index}`"
:value="codes[index].value"
:type="codes[index].inputType"
@keydown="onKeydown(index, $event)"
@paste="onPaste(index, $event)"
@change="onInput(index, $event)"
/>
</div>
</div>
</template>
<script>
export default {
props: [
'modelValue',
'secure',
'length'
],
emits: [
'update:modelValue',
'pincode:confirm'
],
data() {
return {
codes: []
}
},
computed: {
finalPinCode() {
let finalPinCode = '';
for (let i = 0; i < this.codes.length; i++) {
if (this.codes[i].value) {
finalPinCode += this.codes[i].value;
} else {
break;
}
}
return finalPinCode;
}
},
watch: {
'length': function (newValue) {
this.init(newValue, this.modelValue);
},
'modelValue': function (newValue) {
if (newValue === this.finalPinCode) {
return;
}
this.init(this.length, newValue);
},
'codes': {
handler() {
this.$emit('update:modelValue', this.finalPinCode);
},
deep: true
}
},
created() {
this.init(this.length, this.modelValue);
},
methods: {
init(length, value) {
this.codes.length = 0;
for (let i = 0; i < length; i++) {
const code = {
value: '',
inputType: 'tel',
inputTimer: null
};
if (value && value[i]) {
code.value = value[i];
if (this.secure) {
code.inputType = 'password';
}
}
this.codes.push(code);
}
},
autoFillText(index, text) {
let lastIndex = index;
for (let i = index, j = 0; i < this.codes.length && j < text.length; i++, j++) {
if (text[j] < '0' || text[j] > '9') {
this.codes[i].value = '';
this.$forceUpdate();
break;
}
this.codes[i].value = text[j];
this.setInputType(i);
lastIndex = i;
}
this.setFocus(lastIndex);
if (this.finalPinCode.length === this.length) {
this.$emit('pincode:confirm', this.finalPinCode);
}
},
setInputType(index) {
const self = this;
if (!self.secure) {
return;
}
if (!self.codes[index].value) {
self.codes[index].inputType = 'tel';
return;
}
if (self.codes[index].inputTimer) {
return;
}
self.codes[index].inputTimer = setTimeout(() => {
if (self.codes[index].value) {
self.codes[index].inputType = 'password';
} else {
self.codes[index].inputType = 'tel';
}
self.codes[index].inputTimer = null;
}, 300);
},
setFocus(index) {
const refId = `pin-code-input-${index}`;
const ref = this.$refs[refId];
if (ref && ref[0]) {
ref[0].focus();
ref[0].select();
}
},
setPreviousFocus(index) {
if (index > 0) {
this.setFocus(index - 1);
}
},
setNextFocus(index) {
if (index < this.length - 1) {
this.setFocus(index + 1);
}
},
onKeydown(index, event) {
if (event.code === 'Enter' && this.finalPinCode.length === this.length) {
this.$emit('pincode:confirm', this.finalPinCode);
event.preventDefault();
return;
}
if (event.code === 'ArrowLeft' || (event.shiftKey && event.code === 'Tab')) {
this.setPreviousFocus(index);
event.preventDefault();
return;
}
if (event.code === 'ArrowRight' || (!event.shiftKey && event.code === 'Tab')) {
this.setNextFocus(index);
event.preventDefault();
return;
}
if ((event.ctrlKey || event.metaKey) && event.code === 'KeyV') {
return;
}
if (event.code === 'Backspace' || event.code === 'Delete' || event.code === 'Del') {
for (let i = index; i < this.codes.length; i++) {
this.codes[i].value = '';
this.setInputType(i);
}
if (event.code === 'Backspace') {
this.setPreviousFocus(index);
}
event.preventDefault();
return;
}
if (event.code.indexOf('Digit') === 0 && event.code.length === 6) {
this.codes[index].value = event.key;
this.setInputType(index);
this.setNextFocus(index);
if (this.finalPinCode.length === this.length) {
this.$emit('pincode:confirm', this.finalPinCode);
}
}
event.preventDefault();
},
onPaste(index, event) {
if (!event.clipboardData) {
event.preventDefault();
return;
}
const text = event.clipboardData.getData('Text');
if (!text) {
event.preventDefault();
return;
}
this.autoFillText(index, text);
event.preventDefault();
},
onInput(index, event) {
if (!event.target.value) {
event.preventDefault();
return;
}
this.autoFillText(index, event.target.value);
event.preventDefault();
}
}
}
</script>
<style>
.pin-code-input input {
text-align: center;
padding-left: 10px;
padding-right: 10px;
height: 60px !important;
}
.pin-code-input.grid.grid-gap {
--f7-grid-gap: 4px;
}
</style>
+14 -8
View File
@@ -1,15 +1,16 @@
<template>
<f7-sheet style="height:auto" :opened="show"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-page-content>
<f7-sheet swipe-to-close swipe-handler=".swipe-handler" style="height:auto"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<div class="swipe-handler"></div>
<f7-page-content class="margin-top no-padding-top">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px"><b>{{ title }}</b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="no-margin-top margin-bottom-half">{{ hint }}</p>
<f7-list no-hairlines class="no-margin-top margin-bottom">
<p class="no-margin">{{ hint }}</p>
<f7-list no-hairlines class="no-margin">
<f7-list-item class="list-item-pincode-input">
<pincode-input secure :length="6" v-model="currentPinCode" />
<pin-code-input :secure="true" :length="6" v-model="currentPinCode"/>
</f7-list-item>
</f7-list>
<f7-button large fill
@@ -28,13 +29,18 @@
<script>
export default {
props: [
'value',
'modelValue',
'title',
'hint',
'confirmDisabled',
'cancelDisabled',
'show'
],
emits: [
'update:modelValue',
'update:show',
'pincode:confirm'
],
data() {
return {
currentPinCode: ''
@@ -57,7 +63,7 @@ export default {
return;
}
this.$emit('input', this.currentPinCode);
this.$emit('update:modelValue', this.currentPinCode);
this.$emit('pincode:confirm', this.currentPinCode);
},
cancel() {
@@ -1,6 +1,9 @@
<template>
<f7-sheet :class="{ 'tag-selection-huge-sheet': hugeListItemRows }" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
:opened="show" :class="{ 'tag-selection-huge-sheet': hugeListItemRows }"
@sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left">
<f7-link sheet-close :text="$t('Cancel')"></f7-link>
</div>
@@ -12,22 +15,25 @@
<f7-list no-hairlines class="no-margin-top no-margin-bottom" v-if="!items || !items.length || noAvailableTag">
<f7-list-item :title="$t('No available tag')"></f7-list-item>
</f7-list>
<f7-list no-hairlines class="no-margin-top no-margin-bottom" v-else-if="items && items.length && !noAvailableTag">
<f7-list-item checkbox v-for="item in items"
<f7-list dividers no-hairlines class="no-margin-top no-margin-bottom" v-else-if="items && items.length && !noAvailableTag">
<f7-list-item checkbox
v-show="!item.hidden"
:key="item.id"
:class="item.id | tagItemClass(selectedItemIds)"
:class="isChecked(item.id) ? 'list-item-selected' : ''"
:value="item.id"
:checked="item.id | isChecked(selectedItemIds)"
:checked="isChecked(item.id)"
:key="item.id"
v-for="item in items"
@change="changeItemSelection">
<f7-block slot="title" class="no-padding no-margin">
<div class="display-flex">
<f7-icon slot="media" f7="number"></f7-icon>
<div class="tag-selection-list-item list-item-valign-middle padding-left-half">
{{ item.name }}
<template #title>
<f7-block class="no-padding no-margin">
<div class="display-flex">
<f7-icon f7="number"></f7-icon>
<div class="tag-selection-list-item list-item-valign-middle padding-left-half">
{{ item.name }}
</div>
</div>
</div>
</f7-block>
</f7-block>
</template>
</f7-list-item>
</f7-list>
</f7-page-content>
@@ -37,15 +43,19 @@
<script>
export default {
props: [
'value',
'modelValue',
'items',
'show'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
selectedItemIds: self.$utilities.copyArrayTo(self.value, [])
selectedItemIds: self.$utilities.copyArrayTo(self.modelValue, [])
}
},
computed: {
@@ -64,11 +74,11 @@ export default {
},
methods: {
save() {
this.$emit('input', this.selectedItemIds);
this.$emit('update:modelValue', this.selectedItemIds);
this.$emit('update:show', false);
},
onSheetOpen(event) {
this.selectedItemIds = this.$utilities.copyArrayTo(this.value, []);
this.selectedItemIds = this.$utilities.copyArrayTo(this.modelValue, []);
this.scrollToSelectedItem(event.$el);
},
onSheetClosed() {
@@ -95,9 +105,6 @@ export default {
}
},
scrollToSelectedItem(parent) {
const app = this.$f7;
const $$ = app.$;
if (!parent || !parent.length) {
return;
}
@@ -113,8 +120,8 @@ export default {
let lastSelectedItem = selectedItem;
if (selectedItem.length > 0) {
firstSelectedItem = $$(selectedItem[0]);
lastSelectedItem = $$(selectedItem[selectedItem.length - 1]);
firstSelectedItem = this.$ui.elements(selectedItem[0]);
lastSelectedItem = this.$ui.elements(selectedItem[selectedItem.length - 1]);
}
let firstSelectedItemInTop = firstSelectedItem.offset().top - container.offset().top - parseInt(container.css('padding-top'), 10);
@@ -133,26 +140,15 @@ export default {
}
container.scrollTop(targetPos);
}
},
filters: {
isChecked(itemId, selectedItemIds) {
for (let i = 0; i < selectedItemIds.length; i++) {
if (selectedItemIds[i] === itemId) {
},
isChecked(itemId) {
for (let i = 0; i < this.selectedItemIds.length; i++) {
if (this.selectedItemIds[i] === itemId) {
return true;
}
}
return false;
},
tagItemClass(itemId, selectedItemIds) {
for (let i = 0; i < selectedItemIds.length; i++) {
if (selectedItemIds[i] === itemId) {
return 'list-item-selected';
}
}
return '';
}
}
}
@@ -170,4 +166,3 @@ export default {
text-overflow: ellipsis;
}
</style>
self
@@ -1,6 +1,9 @@
<template>
<f7-sheet :class="{ 'tree-view-selection-huge-sheet': hugeTreeViewItems }" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
:class="{ 'tree-view-selection-huge-sheet': hugeTreeViewItems }"
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left"></div>
<div class="right">
<f7-link sheet-close :text="$t('Done')"></f7-link>
@@ -8,26 +11,26 @@
</f7-toolbar>
<f7-page-content>
<f7-treeview>
<f7-treeview-item v-for="item in items"
item-toggle
<f7-treeview-item item-toggle
:opened="isPrimaryItemHasSecondaryValue(item)"
:key="item | itemFieldContent(primaryKeyField, item, false)"
:label="item | itemFieldContent(primaryTitleField, item, primaryTitleI18n)">
<f7-icon slot="media"
:icon="item[primaryIconField] | icon(primaryIconType)"
:style="item[primaryColorField] | iconStyle(primaryIconType, 'var(--default-icon-color)')"
v-if="primaryIconField"></f7-icon>
:label="$tIf((primaryTitleField ? item[primaryTitleField] : item), primaryTitleI18n)"
:key="primaryKeyField ? item[primaryKeyField] : item"
v-for="item in items">
<template #media>
<ItemIcon :icon-type="primaryIconType" :icon-id="item[primaryIconField]"
:color="item[primaryColorField]" v-if="primaryIconField"></ItemIcon>
</template>
<f7-treeview-item v-for="subItem in item[primarySubItemsField]"
selectable
<f7-treeview-item selectable
:selected="isSecondarySelected(subItem)"
:key="subItem | itemFieldContent(secondaryKeyField, subItem, false)"
:label="subItem | itemFieldContent(secondaryTitleField, subItem, secondaryTitleI18n)"
:label="$tIf((secondaryTitleField ? subItem[secondaryTitleField] : subItem), secondaryTitleI18n)"
:key="secondaryKeyField ? subItem[secondaryKeyField] : subItem"
v-for="subItem in item[primarySubItemsField]"
@click="onSecondaryItemClicked(subItem)">
<f7-icon slot="media"
:icon="subItem[secondaryIconField] | icon(secondaryIconType)"
:style="subItem[secondaryColorField] | iconStyle(secondaryIconType, 'var(--default-icon-color)')"
v-if="secondaryIconField"></f7-icon>
<template #media>
<ItemIcon :icon-type="secondaryIconType" :icon-id="subItem[secondaryIconField]"
:color="subItem[secondaryColorField]" v-if="secondaryIconField"></ItemIcon>
</template>
</f7-treeview-item>
</f7-treeview-item>
</f7-treeview>
@@ -38,7 +41,7 @@
<script>
export default {
props: [
'value',
'modelValue',
'primaryKeyField',
'primaryValueField',
'primaryTitleField',
@@ -57,11 +60,15 @@ export default {
'items',
'show'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
currentValue: self.value
currentValue: self.modelValue
}
},
computed: {
@@ -85,7 +92,7 @@ export default {
},
methods: {
onSheetOpen(event) {
this.currentValue = this.value;
this.currentValue = this.modelValue;
this.scrollToSelectedItem(event.$el);
},
onSheetClosed() {
@@ -98,7 +105,7 @@ export default {
this.currentValue = subItem;
}
this.$emit('input', this.currentValue);
this.$emit('update:modelValue', this.currentValue);
this.$emit('update:show', false);
},
isPrimaryItemHasSecondaryValue(primaryItem) {
@@ -1,56 +1,60 @@
<template>
<f7-sheet style="height: auto" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
style="height: auto" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
<f7-toolbar>
<div class="swipe-handler"></div>
<div class="left"></div>
<div class="right">
<f7-link sheet-close :text="$t('Done')"></f7-link>
</div>
</f7-toolbar>
<f7-page-content>
<f7-row>
<f7-col width="50">
<div class="grid grid-cols-2 grid-gap">
<div>
<div class="primary-list-container">
<f7-list no-hairlines class="primary-list no-margin-top no-margin-bottom">
<f7-list dividers no-hairlines class="primary-list no-margin-vertical">
<f7-list-item link="#" no-chevron
v-for="item in items"
:key="item | itemFieldContent(primaryKeyField, item, false)"
:class="{ 'primary-list-item-selected': item === selectedPrimaryItem }"
:value="item | itemFieldContent(primaryValueField, item, false)"
:title="item | itemFieldContent(primaryTitleField, null, primaryTitleI18n)"
:header="item | itemFieldContent(primaryHeaderField, null, primaryHeaderI18n)"
:footer="item | itemFieldContent(primaryFooterField, null, primaryFooterI18n)"
:value="primaryValueField ? item[primaryValueField] : item"
:title="$tIf(item[primaryTitleField], primaryTitleI18n)"
:header="$tIf(item[primaryHeaderField], primaryHeaderI18n)"
:footer="$tIf(item[primaryFooterField], primaryFooterI18n)"
:key="primaryKeyField ? item[primaryKeyField] : item"
v-for="item in items"
@click="onPrimaryItemClicked(item)">
<f7-icon slot="media"
:icon="item[primaryIconField] | icon(primaryIconType)"
:style="item[primaryColorField] | iconStyle(primaryIconType, 'var(--default-icon-color)')"
v-if="primaryIconField"></f7-icon>
<f7-icon slot="after" class="list-item-showing" f7="chevron_right" v-if="item === selectedPrimaryItem"></f7-icon>
<template #media>
<ItemIcon :icon-type="primaryIconType" :icon-id="item[primaryIconField]" :color="item[primaryColorField]"></ItemIcon>
</template>
<template #after>
<f7-icon class="list-item-showing" f7="chevron_right" v-if="item === selectedPrimaryItem"></f7-icon>
</template>
</f7-list-item>
</f7-list>
</div>
</f7-col>
<f7-col width="50">
</div>
<div>
<div class="secondary-list-container">
<f7-list no-hairlines class="secondary-list no-margin-top no-margin-bottom" v-if="selectedPrimaryItem && primarySubItemsField && selectedPrimaryItem[primarySubItemsField]">
<f7-list dividers no-hairlines class="secondary-list no-margin-vertical" v-if="selectedPrimaryItem && primarySubItemsField && selectedPrimaryItem[primarySubItemsField]">
<f7-list-item link="#" no-chevron
v-for="subItem in selectedPrimaryItem[primarySubItemsField]"
:key="subItem | itemFieldContent(secondaryKeyField, subItem, false)"
:class="{ 'secondary-list-item-selected': isSecondarySelected(subItem) }"
:value="subItem | itemFieldContent(secondaryValueField, subItem, false)"
:title="subItem | itemFieldContent(secondaryTitleField, null, secondaryTitleI18n)"
:header="subItem | itemFieldContent(secondaryHeaderField, null, secondaryHeaderI18n)"
:footer="subItem | itemFieldContent(secondaryFooterField, null, secondaryFooterI18n)"
:value="secondaryValueField ? subItem[secondaryValueField] : subItem"
:title="$tIf(subItem[secondaryTitleField], secondaryTitleI18n)"
:header="$tIf(subItem[secondaryHeaderField], secondaryHeaderI18n)"
:footer="$tIf(subItem[secondaryFooterField], secondaryFooterI18n)"
:key="secondaryKeyField ? subItem[secondaryKeyField] : subItem"
v-for="subItem in selectedPrimaryItem[primarySubItemsField]"
@click="onSecondaryItemClicked(subItem)">
<f7-icon slot="media"
:icon="subItem[secondaryIconField] | icon(secondaryIconType)"
:style="subItem[secondaryColorField] | iconStyle(secondaryIconType, 'var(--default-icon-color)')"
v-if="secondaryIconField"></f7-icon>
<f7-icon slot="after" class="list-item-checked-icon" f7="checkmark_alt" v-if="isSecondarySelected(subItem)"></f7-icon>
<template #media>
<ItemIcon :icon-type="secondaryIconType" :icon-id="subItem[secondaryIconField]" :color="subItem[secondaryColorField]"></ItemIcon>
</template>
<template #after>
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="isSecondarySelected(subItem)"></f7-icon>
</template>
</f7-list-item>
</f7-list>
</div>
</f7-col>
</f7-row>
</div>
</div>
</f7-page-content>
</f7-sheet>
</template>
@@ -58,7 +62,7 @@
<script>
export default {
props: [
'value',
'modelValue',
'primaryKeyField',
'primaryValueField',
'primaryTitleField',
@@ -85,12 +89,16 @@ export default {
'items',
'show'
],
emits: [
'update:modelValue',
'update:show'
],
data() {
const self = this;
return {
currentPrimaryValue: self.getPrimaryValueBySecondaryValue(self.value),
currentSecondaryValue: self.value
currentPrimaryValue: self.getPrimaryValueBySecondaryValue(self.modelValue),
currentSecondaryValue: self.modelValue
}
},
computed: {
@@ -126,13 +134,13 @@ export default {
},
methods: {
onSheetOpen(event) {
this.currentPrimaryValue = this.getPrimaryValueBySecondaryValue(this.value);
this.currentSecondaryValue = this.value;
this.currentPrimaryValue = this.getPrimaryValueBySecondaryValue(this.modelValue);
this.currentSecondaryValue = this.modelValue;
this.scrollToSelectedItem(event.$el, '.primary-list-container', 'li.primary-list-item-selected');
this.scrollToSelectedItem(event.$el, '.secondary-list-container', 'li.secondary-list-item-selected');
},
onSheetClosed() {
this.$emit('update:show', false);
this.close();
},
onPrimaryItemClicked(item) {
if (this.primaryValueField) {
@@ -148,8 +156,8 @@ export default {
this.currentSecondaryValue = subItem;
}
this.$emit('input', this.currentSecondaryValue);
this.$emit('update:show', false);
this.$emit('update:modelValue', this.currentSecondaryValue);
this.close();
},
isSecondarySelected(subItem) {
if (this.secondaryValueField) {
@@ -226,6 +234,9 @@ export default {
}
container.scrollTop(targetPos);
},
close() {
this.$emit('update:show', false);
}
}
}