|
1 | 1 | <template> |
2 | 2 | <f7-sheet swipe-to-close swipe-handler=".swipe-handler" |
3 | | - :opened="show" |
| 3 | + :style="sheetStyle" :opened="show" |
4 | 4 | @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed"> |
5 | 5 | <f7-toolbar class="toolbar-with-swipe-handler"> |
6 | 6 | <div class="swipe-handler"></div> |
7 | 7 | <div class="left"> |
8 | 8 | <f7-link sheet-close icon-f7="xmark"></f7-link> |
9 | 9 | </div> |
| 10 | + <f7-segmented strong round class="width-100"> |
| 11 | + <f7-button :active="currentTab === 'system'" @click="showSystemColors">{{ tt('System Colors') }}</f7-button> |
| 12 | + <f7-button :active="currentTab === 'custom'" @click="showCustomColor">{{ tt('Custom Color') }}</f7-button> |
| 13 | + </f7-segmented> |
10 | 14 | </f7-toolbar> |
11 | 15 | <f7-page-content> |
12 | | - <f7-block class="margin-vertical no-padding"> |
| 16 | + <f7-block class="margin-vertical no-padding" v-if="currentTab === 'system'"> |
13 | 17 | <div class="grid padding-vertical-half padding-horizontal-half" |
14 | | - :class="{ 'row-has-selected-item': hasSelectedIcon(row) }" |
| 18 | + :class="{ 'row-has-selected-item': hasSelectedColor(row) }" |
15 | 19 | :style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`" |
16 | 20 | :key="idx" v-for="(row, idx) in allColorRows"> |
17 | 21 | <div class="text-align-center" :key="colorInfo.color" v-for="colorInfo in row"> |
|
23 | 27 | </div> |
24 | 28 | </div> |
25 | 29 | </f7-block> |
| 30 | + <f7-block class="no-margin no-padding" v-if="currentTab === 'custom'"> |
| 31 | + <div class="custom-color-picker" ref="customColorPickerContainer"></div> |
| 32 | + </f7-block> |
26 | 33 | </f7-page-content> |
27 | 34 | </f7-sheet> |
28 | 35 | </template> |
29 | 36 |
|
30 | 37 | <script setup lang="ts"> |
31 | | -import { ref, computed } from 'vue'; |
| 38 | +import { ref, computed, useTemplateRef, nextTick, onBeforeUnmount } from 'vue'; |
| 39 | +import type { ColorPicker } from 'framework7/types'; |
32 | 40 |
|
| 41 | +import { useI18n } from '@/locales/helpers.ts'; |
33 | 42 | import type { ColorValue, ColorInfo } from '@/core/color.ts'; |
34 | 43 | import { arrayContainsFieldValue } from '@/lib/common.ts'; |
35 | 44 | import { getColorsInRows } from '@/lib/color.ts'; |
36 | 45 | import { scrollToSelectedItem } from '@/lib/ui/common.ts'; |
37 | | -import { type Framework7Dom } from '@/lib/ui/mobile.ts'; |
| 46 | +import { type Framework7Dom, createInlineColorPicker } from '@/lib/ui/mobile.ts'; |
38 | 47 |
|
39 | 48 | const props = defineProps<{ |
40 | 49 | modelValue: ColorValue; |
41 | 50 | show: boolean; |
42 | 51 | columnCount?: number; |
43 | | - allColorInfos: ColorValue[]; |
| 52 | + allSystemColorInfos: ColorValue[]; |
44 | 53 | }>(); |
45 | 54 |
|
46 | 55 | const emit = defineEmits<{ |
47 | 56 | (e: 'update:modelValue', value: ColorValue): void; |
48 | 57 | (e: 'update:show', value: boolean): void; |
49 | 58 | }>(); |
50 | 59 |
|
| 60 | +const { tt } = useI18n(); |
| 61 | +
|
| 62 | +const customColorPickerContainer = useTemplateRef<HTMLElement>('customColorPickerContainer'); |
| 63 | +
|
| 64 | +let customColorPicker: ColorPicker.ColorPicker | null = null; |
| 65 | +
|
| 66 | +const currentTab = ref<'system' | 'custom'>(isSystemColor(props.modelValue) ? 'system' : 'custom'); |
51 | 67 | const currentValue = ref<ColorValue>(props.modelValue); |
52 | 68 | const itemPerRow = ref<number>(props.columnCount || 7); |
| 69 | +const customSheetHeight = ref<number>(420); |
| 70 | +
|
| 71 | +const allColorRows = computed<ColorInfo[][]>(() => getColorsInRows(props.allSystemColorInfos, itemPerRow.value)); |
| 72 | +
|
| 73 | +const sheetStyle = computed<Record<string, string> | undefined>(() => { |
| 74 | + const style: Record<string, string> = {}; |
| 75 | +
|
| 76 | + if (currentTab.value === 'custom' && customSheetHeight.value) { |
| 77 | + style['height'] = `${customSheetHeight.value}px`; |
| 78 | + } |
53 | 79 |
|
54 | | -const allColorRows = computed<ColorInfo[][]>(() => getColorsInRows(props.allColorInfos, itemPerRow.value)); |
| 80 | + return style; |
| 81 | +}); |
55 | 82 |
|
56 | 83 | function onColorClicked(colorInfo: ColorInfo): void { |
57 | 84 | currentValue.value = colorInfo.color; |
58 | 85 | emit('update:modelValue', currentValue.value); |
59 | 86 | } |
60 | 87 |
|
61 | | -function hasSelectedIcon(row: ColorInfo[]): boolean { |
62 | | - return arrayContainsFieldValue(row, 'id', currentValue.value); |
| 88 | +function isSystemColor(value: ColorValue): boolean { |
| 89 | + return props.allSystemColorInfos.includes(value); |
| 90 | +} |
| 91 | +
|
| 92 | +function hasSelectedColor(row: ColorInfo[]): boolean { |
| 93 | + return arrayContainsFieldValue(row, 'color', currentValue.value); |
| 94 | +} |
| 95 | +
|
| 96 | +function createCustomColorPicker(): void { |
| 97 | + nextTick(() => { |
| 98 | + if (!customColorPickerContainer.value || customColorPicker) { |
| 99 | + return; |
| 100 | + } |
| 101 | +
|
| 102 | + customColorPicker = createInlineColorPicker( |
| 103 | + customColorPickerContainer.value, |
| 104 | + currentValue.value, |
| 105 | + (value: ColorValue) => { |
| 106 | + currentValue.value = value; |
| 107 | + emit('update:modelValue', currentValue.value); |
| 108 | + } |
| 109 | + ); |
| 110 | +
|
| 111 | + nextTick(() => { |
| 112 | + const pageContent = customColorPickerContainer.value?.closest('.page-content'); |
| 113 | +
|
| 114 | + if (pageContent) { |
| 115 | + customSheetHeight.value = Math.ceil(pageContent.scrollHeight); |
| 116 | + } |
| 117 | + }); |
| 118 | + }); |
| 119 | +} |
| 120 | +
|
| 121 | +function destroyCustomColorPicker(): void { |
| 122 | + if (customColorPicker) { |
| 123 | + customColorPicker.destroy(); |
| 124 | + customColorPicker = null; |
| 125 | + } |
| 126 | +
|
| 127 | + if (customColorPickerContainer.value) { |
| 128 | + customColorPickerContainer.value.innerHTML = ''; |
| 129 | + } |
| 130 | +} |
| 131 | +
|
| 132 | +function showSystemColors(): void { |
| 133 | + currentTab.value = 'system'; |
| 134 | + destroyCustomColorPicker(); |
| 135 | +} |
| 136 | +
|
| 137 | +function showCustomColor(): void { |
| 138 | + currentTab.value = 'custom'; |
| 139 | + createCustomColorPicker(); |
63 | 140 | } |
64 | 141 |
|
65 | 142 | function onSheetOpen(event: { $el: Framework7Dom }): void { |
66 | 143 | currentValue.value = props.modelValue; |
67 | | - scrollToSelectedItem(event.$el[0], '.sheet-modal-inner', '.page-content', '.row-has-selected-item'); |
| 144 | + currentTab.value = isSystemColor(currentValue.value) ? 'system' : 'custom'; |
| 145 | +
|
| 146 | + if (currentTab.value === 'custom') { |
| 147 | + createCustomColorPicker(); |
| 148 | + } else { |
| 149 | + scrollToSelectedItem(event.$el[0], '.sheet-modal-inner', '.page-content', '.row-has-selected-item'); |
| 150 | + } |
68 | 151 | } |
69 | 152 |
|
70 | 153 | function onSheetClosed(): void { |
| 154 | + destroyCustomColorPicker(); |
71 | 155 | emit('update:show', false); |
72 | 156 | } |
| 157 | +
|
| 158 | +onBeforeUnmount(() => { |
| 159 | + destroyCustomColorPicker(); |
| 160 | +}); |
73 | 161 | </script> |
| 162 | + |
| 163 | +<style> |
| 164 | +.custom-color-picker { |
| 165 | + .color-picker { |
| 166 | + width: 100%; |
| 167 | +
|
| 168 | + .color-picker-hex-value { |
| 169 | + width: 100%; |
| 170 | + } |
| 171 | + } |
| 172 | +} |
| 173 | +</style> |
0 commit comments