Skip to content

Commit 9da1135

Browse files
authored
Merge pull request #494 from appuniversum/chore/more-gts
More Glint / TS conversion
2 parents f5bcb51 + f9ed225 commit 9da1135

21 files changed

+807
-511
lines changed

addon/components/au-date-picker.gjs addon/components/au-date-picker.gts

+95-45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { AuLabel } from '@appuniversum/ember-appuniversum';
21
import {
32
formatDate,
43
isIsoDateString,
@@ -11,29 +10,42 @@ import { guidFor } from '@ember/object/internals';
1110
import Component from '@glimmer/component';
1211
import { tracked } from '@glimmer/tracking';
1312
import { modifier } from 'ember-modifier';
13+
import AuLabel from './au-label';
14+
import type { DuetDatePickerChangeEvent } from '@duetds/date-picker/dist/types/components/duet-date-picker/duet-date-picker';
15+
import type { DuetLocalizedText } from '@duetds/date-picker/dist/types/components/duet-date-picker/date-localization';
16+
import type { DuetDateAdapter } from '@duetds/date-picker/dist/types/components/duet-date-picker/date-adapter';
1417

15-
const props = modifier(
16-
function props(element, positional, properties) {
17-
for (let propertyName in properties) {
18-
element[propertyName] = properties[propertyName];
19-
}
20-
},
21-
{ eager: false },
22-
);
18+
type IsoDate = string;
19+
type Adapter = DuetDateAdapter;
20+
type Localization = DuetLocalizedText;
21+
type DayOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6; // Based on this enum: https://github.com/duetds/date-picker/blob/a89499198d6e5555073bb0dec3a3dab9a5b3648b/src/components/duet-date-picker/date-utils.ts#L3
2322

24-
const DEFAULT_ADAPTER = {
25-
parse: function (value = '', createDate) {
26-
let dateRegex = /^(\d{1,2})-(\d{1,2})-(\d{4})$/;
23+
export interface AuDatePickerSignature {
24+
Args: {
25+
alignment?: 'top';
26+
adapter?: Adapter;
27+
buttonLabel?: string;
28+
disabled?: boolean;
29+
error?: boolean;
30+
'first-day'?: DayOfWeek;
31+
firstDay?: DayOfWeek;
32+
id?: string;
33+
label?: string;
34+
localization?: Localization;
35+
max?: IsoDate | Date;
36+
min?: IsoDate | Date;
37+
value?: IsoDate | Date;
38+
warning?: boolean;
39+
onChange?: (isoDate: IsoDate | null, date: Date | null) => void;
40+
};
41+
Element: HTMLDuetDatePickerElement;
42+
}
2743

28-
const matches = value.match(dateRegex);
29-
if (matches) {
30-
return createDate(matches[3], matches[2], matches[1]);
31-
}
32-
},
33-
format: formatDate,
34-
};
44+
type DayNames = [string, string, string, string, string, string, string];
45+
// prettier-ignore
46+
type MonthNames = [string, string, string, string, string, string, string, string, string, string, string, string];
3547

36-
const DEFAULT_LOCALIZATION = {
48+
const DEFAULT_LOCALIZATION: Localization = {
3749
dayNames: getLocalizedDays(),
3850
monthNames: getLocalizedMonths(),
3951
monthNamesShort: getLocalizedMonths('short'),
@@ -45,18 +57,40 @@ const DEFAULT_LOCALIZATION = {
4557
monthSelectLabel: 'Maand',
4658
yearSelectLabel: 'Jaar',
4759
closeLabel: 'Sluit venster',
48-
keyboardInstruction: 'Gebruik de pijltjestoetsen om te navigeren',
4960
calendarHeading: 'Kies een datum',
61+
locale: 'nl-BE',
5062
};
5163

52-
export default class AuDatePicker extends Component {
53-
@asIsoDate value;
54-
@asIsoDate min;
55-
@asIsoDate max;
64+
const DEFAULT_ADAPTER: Adapter = {
65+
parse: function (
66+
value = '',
67+
createDate: (day: string, month: string, year: string) => Date,
68+
) {
69+
const dateRegex = /^(\d{1,2})-(\d{1,2})-(\d{4})$/;
70+
71+
const matches = value.match(dateRegex);
72+
if (matches) {
73+
return createDate(
74+
matches[3] as string,
75+
matches[2] as string,
76+
matches[1] as string,
77+
);
78+
}
79+
},
80+
format: formatDate,
81+
};
82+
83+
export default class AuDatePicker extends Component<AuDatePickerSignature> {
84+
// @ts-expect-error TODO: Something is wrong with the decorator types, but I'm not sure how to fix it.
85+
@asIsoDate declare value: IsoDate;
86+
// @ts-expect-error TODO: Something is wrong with the decorator types, but I'm not sure how to fix it.
87+
@asIsoDate declare min: IsoDate;
88+
// @ts-expect-error TODO: Something is wrong with the decorator types, but I'm not sure how to fix it.
89+
@asIsoDate declare max: IsoDate;
5690
@tracked isInitialized = false;
5791

58-
constructor() {
59-
super(...arguments);
92+
constructor(owner: unknown, args: AuDatePickerSignature['Args']) {
93+
super(owner, args);
6094
this.registerDuetDatePicker();
6195
}
6296

@@ -101,9 +135,13 @@ export default class AuDatePicker extends Component {
101135
else return '';
102136
}
103137

138+
get firstDayOfWeek() {
139+
return this.args.firstDay || this.args['first-day'];
140+
}
141+
104142
@action
105-
handleDuetDateChange(event) {
106-
let wasDatePickerCleared = !event.detail.value;
143+
handleDuetDateChange(event: CustomEvent<DuetDatePickerChangeEvent>) {
144+
const wasDatePickerCleared = !event.detail.value;
107145
if (wasDatePickerCleared) {
108146
this.args.onChange?.(null, null);
109147
} else {
@@ -112,7 +150,7 @@ export default class AuDatePicker extends Component {
112150
}
113151

114152
async registerDuetDatePicker() {
115-
if (typeof FastBoot === 'undefined') {
153+
if (typeof globalThis.FastBoot === 'undefined') {
116154
const { defineCustomElements: registerDuetDatePicker } = await import(
117155
'@duetds/date-picker/custom-element'
118156
);
@@ -141,8 +179,9 @@ export default class AuDatePicker extends Component {
141179
value={{this.value}}
142180
min={{this.min}}
143181
max={{this.max}}
144-
first-day-of-week={{@first-day}}
182+
first-day-of-week={{this.firstDayOfWeek}}
145183
data-test-au-date-picker-component
184+
{{! @glint-expect-error duetChange is a custom event but the types expect Event instead}}
146185
{{on "duetChange" this.handleDuetDateChange}}
147186
{{props localization=this.localization dateAdapter=this.adapter}}
148187
...attributes
@@ -152,7 +191,7 @@ export default class AuDatePicker extends Component {
152191
</template>
153192
}
154193

155-
function validateAdapter(adapterArg) {
194+
function validateAdapter(adapterArg?: Adapter) {
156195
assert(
157196
`The @adapter argument needs to be an object but it is a "${typeof adapterArg}"`,
158197
Boolean(adapterArg) && typeof adapterArg === 'object',
@@ -166,7 +205,7 @@ function validateAdapter(adapterArg) {
166205
});
167206
}
168207

169-
function validateLocalization(localizationArg) {
208+
function validateLocalization(localizationArg?: Localization) {
170209
assert(
171210
`The @localization argument needs to be an object but it is a "${typeof localizationArg}"`,
172211
Boolean(localizationArg) && typeof localizationArg === 'object',
@@ -180,10 +219,10 @@ function validateLocalization(localizationArg) {
180219
});
181220
}
182221

183-
function asIsoDate(target, key /*, descriptor */) {
222+
function asIsoDate(target: unknown, key: string /*, descriptor */) {
184223
return {
185-
get() {
186-
let argValue = this.args[key];
224+
get(this: AuDatePicker): string | undefined {
225+
const argValue = (this.args as { [key: string]: unknown })[key];
187226

188227
if (!argValue) {
189228
return;
@@ -207,23 +246,34 @@ function asIsoDate(target, key /*, descriptor */) {
207246
};
208247
}
209248

210-
function getLocalizedMonths(monthFormat = 'long') {
211-
let someYear = 2021;
249+
function getLocalizedMonths(monthFormat = 'long'): MonthNames {
250+
const someYear = 2021;
212251
return [...Array(12).keys()].map((monthIndex) => {
213-
let date = new Date(someYear, monthIndex);
252+
const date = new Date(someYear, monthIndex);
214253
return intl({ month: monthFormat }).format(date);
215-
});
254+
}) as MonthNames;
216255
}
217256

218-
function getLocalizedDays(weekdayFormat = 'long') {
219-
let someSunday = new Date('2021-01-03');
257+
function getLocalizedDays(weekdayFormat = 'long'): DayNames {
258+
const someSunday = new Date('2021-01-03');
220259
return [...Array(7).keys()].map((index) => {
221-
let weekday = new Date(someSunday.getTime());
260+
const weekday = new Date(someSunday.getTime());
222261
weekday.setDate(someSunday.getDate() + index);
223262
return intl({ weekday: weekdayFormat }).format(weekday);
224-
});
263+
}) as DayNames;
225264
}
226265

227-
function intl(options) {
266+
function intl(options: object) {
228267
return new Intl.DateTimeFormat('nl-BE', options);
229268
}
269+
270+
const props = modifier(function props(
271+
element: HTMLElement,
272+
positional,
273+
properties: { [key: string]: unknown },
274+
) {
275+
for (const propertyName in properties) {
276+
(element as unknown as { [key: string]: unknown })[propertyName] =
277+
properties[propertyName];
278+
}
279+
});

addon/components/au-dropdown.gjs addon/components/au-dropdown.gts

+38-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { AuButton } from '@appuniversum/ember-appuniversum';
2-
import FloatingUiModifier from '@appuniversum/ember-appuniversum/private/modifiers/floating-ui';
31
import { hash } from '@ember/helper';
42
import { on } from '@ember/modifier';
53
import { action } from '@ember/object';
@@ -8,26 +6,41 @@ import { tracked } from '@glimmer/tracking';
86
import { focusTrap } from 'ember-focus-trap';
97
import { modifier } from 'ember-modifier';
108
import { ChevronDownIcon } from './icons/chevron-down';
9+
import AuButton from './au-button';
10+
import type { AuButtonSignature } from './au-button';
11+
import floatingUi from '../private/modifiers/floating-ui';
12+
13+
export interface AuDropdownSignature {
14+
Args: {
15+
alignment?: 'left' | 'right';
16+
alert?: boolean;
17+
hideText?: boolean;
18+
icon?: AuButtonSignature['Args']['icon'];
19+
iconAlignment?: AuButtonSignature['Args']['iconAlignment'];
20+
onClose?: () => unknown;
21+
size?: AuButtonSignature['Args']['size'];
22+
skin?: AuButtonSignature['Args']['skin'];
23+
title?: string;
24+
};
25+
Blocks: {
26+
default: [];
27+
};
28+
Element: HTMLDivElement;
29+
}
1130

12-
export default class AuDropdown extends Component {
13-
@tracked referenceElement = undefined;
14-
@tracked arrowElement = undefined;
31+
export default class AuDropdown extends Component<AuDropdownSignature> {
32+
// We use declare here, so TS doesn't consider `undefined` as part of the type since the initialisation happens after the constructor.
33+
@tracked declare referenceElement: HTMLElement;
34+
@tracked declare arrowElement: HTMLElement;
1535
@tracked dropdownOpen = false;
16-
floatingUi = FloatingUiModifier;
17-
18-
reference = modifier(
19-
(element) => {
20-
this.referenceElement = element;
21-
},
22-
{ eager: false },
23-
);
24-
25-
arrow = modifier(
26-
(element) => {
27-
this.arrowElement = element;
28-
},
29-
{ eager: false },
30-
);
36+
37+
reference = modifier((element: HTMLElement) => {
38+
this.referenceElement = element;
39+
});
40+
41+
arrow = modifier((element: HTMLElement) => {
42+
this.arrowElement = element;
43+
});
3144

3245
@action
3346
openDropdown() {
@@ -50,8 +63,10 @@ export default class AuDropdown extends Component {
5063
}
5164

5265
@action
53-
clickOutsideDeactivates(event) {
54-
let isClosedByToggleButton = this.referenceElement.contains(event.target);
66+
clickOutsideDeactivates(event: Event) {
67+
const isClosedByToggleButton = this.referenceElement?.contains(
68+
event.target as HTMLElement,
69+
);
5570

5671
if (!isClosedByToggleButton) {
5772
this.closeDropdown();
@@ -113,7 +128,7 @@ export default class AuDropdown extends Component {
113128
</AuButton>
114129
{{#if this.dropdownOpen}}
115130
<div
116-
{{this.floatingUi
131+
{{floatingUi
117132
this.referenceElement
118133
this.arrowElement
119134
defaultPlacement=this.alignment

0 commit comments

Comments
 (0)