Skip to content

Commit daa4b70

Browse files
authored
Merge pull request #8075 from nextcloud-libraries/backport/6821/stable8
[stable8] refactor(NcTimezonePicker): migrate component to Typescript
2 parents e31b06d + 5933ef8 commit daa4b70

File tree

7 files changed

+164
-274
lines changed

7 files changed

+164
-274
lines changed

l10n/messages.pot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ msgstr ""
193193
msgid "Frequently used"
194194
msgstr ""
195195

196+
#. TRANSLATORS: This refers to global timezones in the timezone picker
196197
msgid "Global"
197198
msgstr ""
198199

src/components/NcDateTimePicker/NcDateTimePicker.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ import { t } from '../../l10n.js'
181181
import GenRandomId from '../../utils/GenRandomId.js'
182182
import { logger } from '../../utils/logger.ts'
183183
import NcPopover from '../NcPopover/index.js'
184-
import NcTimezonePicker from '../NcTimezonePicker/index.js'
184+
import NcTimezonePicker from '../NcTimezonePicker/index.ts'
185185
186186
import './index.scss'
187187

src/components/NcTimezonePicker/NcTimezonePicker.vue

Lines changed: 160 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<template>
1010
<span>
1111
<NcTimezonePicker v-model="tz" />
12+
{{ tz }}
1213
</span>
1314
</template>
1415
<script>
@@ -23,192 +24,175 @@ export default {
2324
```
2425
</docs>
2526

26-
<template>
27-
<NcSelect
28-
:aria-label-combobox="t('Search for time zone')"
29-
:clearable="false"
30-
:filter-by="filterBy"
31-
:multiple="false"
32-
:options="options"
33-
:placeholder="placeholder"
34-
:selectable="isSelectable"
35-
:uid="uid"
36-
:value="selectedTimezone"
37-
label="label"
38-
@option:selected="change" />
39-
</template>
27+
<script setup lang="ts">
28+
import type {
29+
IContinent,
30+
IRegion,
31+
ITimezone,
32+
} from '@nextcloud/timezones'
4033
41-
<script>
42-
import { useModelMigration } from '../../composables/useModelMigration.ts'
43-
import { t } from '../../l10n.js'
44-
import GenRandomId from '../../utils/GenRandomId.js'
45-
import NcSelect from '../NcSelect/index.js'
4634
import {
4735
getReadableTimezoneName,
4836
getSortedTimezoneList,
49-
} from './timezone.js'
50-
import getTimezoneManager from './timezoneDataProviderService.js'
51-
52-
export default {
53-
name: 'NcTimezonePicker',
54-
components: {
55-
NcSelect,
56-
},
57-
58-
model: {
59-
prop: 'modelValue',
60-
event: 'update:modelValue',
61-
},
62-
63-
props: {
64-
/**
65-
* An array of additional timezones to include with the standard database. Useful if there is a custom timezone, e.g. read from user data
66-
*/
67-
additionalTimezones: {
68-
type: Array,
69-
default: () => [],
70-
},
71-
72-
/**
73-
* Removed in v9 - use `modelValue` (`v-model`) instead
74-
*
75-
* @deprecated
76-
*/
77-
value: {
78-
type: String,
79-
default: undefined,
80-
},
81-
82-
/**
83-
* The selected timezone. Use v-model for two-way binding. The default timezone is floating, which means a time independent of timezone. See https://icalendar.org/CalDAV-Access-RFC-4791/7-3-date-and-floating-time.html for details.
84-
*/
85-
modelValue: {
86-
type: String,
87-
default: 'floating',
88-
},
89-
90-
/**
91-
* ID of the inner vue-select element, can be used for labels like: `vs-${uid}__combobox`
92-
*/
93-
uid: {
94-
type: [String, Number],
95-
default: () => `tz-${GenRandomId(5)}`,
96-
},
97-
},
98-
99-
emits: [
100-
/**
101-
* Removed in v9 - use `update:modelValue` (`v-model`) instead
102-
*
103-
* @deprecated
104-
*/
105-
'input',
106-
/**
107-
* Two-way binding of the value prop. Use v-model="selectedTimezone" for two-way binding
108-
*/
109-
'update:modelValue',
110-
/** Same as update:modelValue for Vue 2 compatibility */
111-
'update:model-value',
112-
],
113-
114-
setup() {
115-
const model = useModelMigration('value', 'input')
116-
return {
117-
model,
118-
}
37+
} from '@nextcloud/timezones'
38+
import { computed } from 'vue'
39+
import { useModelMigration } from '../../composables/useModelMigration.ts'
40+
import { t } from '../../l10n.js'
41+
import { createElementId } from '../../utils/createElementId.ts'
42+
import NcSelect from '../NcSelect/index.js'
43+
import getTimezoneManager from './timezoneDataProviderService.ts'
44+
45+
const props = withDefaults(defineProps<{
46+
/**
47+
* An array of additional timezones to include with the standard database. Useful if there is a custom timezone, e.g. read from user data
48+
*/
49+
additionalTimezones?: ITimezone[]
50+
/**
51+
* Removed in v9 - use `modelValue` (`v-model`) instead
52+
*
53+
* @deprecated
54+
*/
55+
value?: string
56+
/**
57+
* The selected timezone. Use v-model for two-way binding. The default timezone is floating, which means a time independent of timezone. See https://icalendar.org/CalDAV-Access-RFC-4791/7-3-date-and-floating-time.html for details.
58+
*/
59+
modelValue?: string
60+
/**
61+
* ID of the inner vue-select element, can be used for labels like: `vs-${uid}__combobox`
62+
*/
63+
uid?: string
64+
}>(), {
65+
additionalTimezones: () => [],
66+
value: undefined,
67+
modelValue: 'floating',
68+
uid: createElementId(),
69+
})
70+
71+
defineEmits<{
72+
/**
73+
* Removed in v9 - use `update:modelValue` (`v-model`) instead
74+
*
75+
* @deprecated
76+
*/
77+
(event: 'input', value: string): void
78+
/**
79+
* Two-way binding of the value prop. Use v-model="selectedTimezone" for two-way binding
80+
*/
81+
(event: 'update:modelValue', value: string): void
82+
/** Same as update:modelValue for Vue 2 compatibility */
83+
(event: 'update:model-value', value: string): void
84+
}>()
85+
86+
/**
87+
* The selected timezone.
88+
* Use v-model for two-way binding.
89+
* The default timezone is floating, which means a time independent of timezone. See https://icalendar.org/CalDAV-Access-RFC-4791/7-3-date-and-floating-time.html for details.
90+
*/
91+
const model = useModelMigration<string>('value', 'input')
92+
93+
const selectedTimezone = computed({
94+
set(timezone: IRegion) {
95+
model.value = timezone.timezoneId
11996
},
120-
121-
computed: {
122-
placeholder() {
123-
return t('Type to search time zone')
124-
},
125-
126-
selectedTimezone() {
127-
for (const additionalTimezone of this.additionalTimezones) {
128-
if (additionalTimezone.timezoneId === this.model) {
129-
return additionalTimezone
97+
get(): IRegion {
98+
for (const additionalTimezone of props.additionalTimezones) {
99+
if (additionalTimezone.timezoneId === model.value) {
100+
return {
101+
cities: [],
102+
...additionalTimezone,
130103
}
131104
}
105+
}
132106
133-
return {
134-
label: getReadableTimezoneName(this.model),
135-
timezoneId: this.model,
136-
}
137-
},
138-
139-
options() {
140-
const timezoneManager = getTimezoneManager()
141-
const timezoneList = getSortedTimezoneList(timezoneManager.listAllTimezones(), this.additionalTimezones)
142-
/**
143-
* Since NcSelect does not support groups,
144-
* we create an object with the grouped timezones and continent labels.
145-
*
146-
* NOTE for now we are removing the grouping from the fields to fix an accessibility issue
147-
* in the future, other options can be introduced to better display the different areas
148-
*/
149-
let timezonesGrouped = []
150-
Object.values(timezoneList).forEach((group) => {
151-
// Add an entry as group label
152-
// timezonesGrouped.push({
153-
// label: group.continent,
154-
// timezoneId: `tz-group__${group.continent}`,
155-
// regions: group.regions,
156-
// })
157-
timezonesGrouped = timezonesGrouped.concat(group.regions)
158-
})
159-
return timezonesGrouped
160-
},
107+
return {
108+
label: getReadableTimezoneName(model.value),
109+
timezoneId: model.value,
110+
cities: [],
111+
}
161112
},
113+
})
114+
115+
const options = computed(() => {
116+
const timezoneManager = getTimezoneManager()
117+
const timezoneList: IContinent[] = getSortedTimezoneList(
118+
timezoneManager.listAllTimezones(),
119+
props.additionalTimezones,
120+
t('Global'), // TRANSLATORS: This refers to global timezones in the timezone picker
121+
)
122+
/**
123+
* Since NcSelect does not support groups,
124+
* we create an object with the grouped timezones and continent labels.
125+
*
126+
* NOTE for now we are removing the grouping from the fields to fix an accessibility issue
127+
* in the future, other options can be introduced to better display the different areas
128+
*/
129+
const timezonesGrouped: IRegion[] = []
130+
for (const group of Object.values(timezoneList)) {
131+
// Add an entry as group label
132+
// const continent = `tz-group__${group.continent}`
133+
// timezonesGrouped.push({
134+
// label: group.continent,
135+
// continent,
136+
// timezoneId: continent,
137+
// regions: group.regions,
138+
// })
139+
timezonesGrouped.push(...group.regions)
140+
}
141+
return timezonesGrouped
142+
})
143+
144+
/**
145+
* Returns whether this is a continent label,
146+
* or an actual timezone. Continent labels are not selectable.
147+
*
148+
* @param option The option
149+
*/
150+
function isSelectable(option: IRegion): boolean {
151+
return !option.timezoneId.startsWith('tz-group__')
152+
}
162153
163-
methods: {
164-
t,
165-
166-
change(newValue) {
167-
if (!newValue) {
168-
return
169-
}
170-
171-
this.model = newValue.timezoneId
172-
},
173-
174-
/**
175-
* Returns whether this is a continent label,
176-
* or an actual timezone. Continent labels are not selectable.
177-
*
178-
* @param {string} option The option
179-
* @return {boolean}
180-
*/
181-
isSelectable(option) {
182-
return !option.timezoneId.startsWith('tz-group__')
183-
},
184-
185-
/**
186-
* Function to filter the timezone list.
187-
* We search in the timezoneId, so both continent and region names can be matched.
188-
*
189-
* @param {object} option The timezone option
190-
* @param {string} label The label of the timezone
191-
* @param {string} search The search string
192-
* @return {boolean}
193-
*/
194-
filterBy(option, label, search) {
195-
// We split the search term in case one searches "<continent> <region>".
196-
const terms = search.trim().split(' ')
197-
198-
// For the continent labels, we have to check if one region matches every search term.
199-
if (option.timezoneId.startsWith('tz-group__')) {
200-
return option.regions.some((region) => {
201-
return this.matchTimezoneId(region.timezoneId, terms)
202-
})
203-
}
204-
205-
// For a region, every search term must be found.
206-
return this.matchTimezoneId(option.timezoneId, terms)
207-
},
154+
/**
155+
* Function to filter the timezone list.
156+
* We search in the timezoneId, so both continent and region names can be matched.
157+
*
158+
* @param option - The timezone option
159+
* @param label - The label of the timezone
160+
* @param search - The search string
161+
*/
162+
function filterBy(option: IContinent | IRegion, label: string, search: string): boolean {
163+
// We split the search term in case one searches "<continent> <region>".
164+
const terms = search.trim().split(' ')
165+
166+
// For the continent labels, we have to check if one region matches every search term.
167+
if ('continent' in option) {
168+
return option.regions.some((region) => {
169+
return matchTimezoneId(region.timezoneId, terms)
170+
})
171+
}
172+
173+
// For a region, every search term must be found.
174+
return matchTimezoneId(option.timezoneId, terms)
175+
}
208176
209-
matchTimezoneId(timezoneId, terms) {
210-
return terms.every((term) => timezoneId.toLowerCase().includes(term.toLowerCase()))
211-
},
212-
},
177+
/**
178+
* @param timezoneId - The timezone id to check
179+
* @param terms - Terms to validate
180+
*/
181+
function matchTimezoneId(timezoneId: string, terms: string[]): boolean {
182+
return terms.every((term) => timezoneId.toLowerCase().includes(term.toLowerCase()))
213183
}
214184
</script>
185+
186+
<template>
187+
<NcSelect
188+
v-model="selectedTimezone"
189+
:aria-label-combobox="t('Search for time zone')"
190+
:clearable="false"
191+
:filter-by="filterBy"
192+
:multiple="false"
193+
:options="options"
194+
:placeholder="t('Type to search time zone')"
195+
:selectable="isSelectable"
196+
:uid="uid"
197+
label="label" />
198+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6+
export type * from './NcTimezonePicker.vue'
67
export { default } from './NcTimezonePicker.vue'

0 commit comments

Comments
 (0)