Introduce typed templates with Glint (#714)
This commit is contained in:
parent
146f531a37
commit
76586f165d
63 changed files with 859 additions and 216 deletions
|
@ -4,8 +4,6 @@ import loadInitializers from 'ember-load-initializers';
|
|||
import config from 'croodle/config/environment';
|
||||
|
||||
export default class App extends Application {
|
||||
LOG_TRANSITIONS = true;
|
||||
|
||||
modulePrefix = config.modulePrefix;
|
||||
podModulePrefix = config.podModulePrefix;
|
||||
Resolver = Resolver;
|
||||
|
|
16
app/components/back-button.ts
Normal file
16
app/components/back-button.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
|
||||
interface BackButtonSignature {
|
||||
Args: { onClick?: () => void };
|
||||
Element: HTMLButtonElement;
|
||||
}
|
||||
|
||||
const BackButton = templateOnlyComponent<BackButtonSignature>();
|
||||
|
||||
export default BackButton;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
BackButton: typeof BackButton;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{{#let @form as |form|}}
|
||||
<form.element
|
||||
{{#let @formElement as |FormElement|}}
|
||||
<FormElement
|
||||
@label={{t "create.options.dates.label"}}
|
||||
@property="options"
|
||||
data-test-form-element-for="days"
|
||||
|
@ -17,10 +17,7 @@
|
|||
<InlineDatepicker
|
||||
@center={{this.calendarCenter}}
|
||||
@selectedDays={{this.selectedDays}}
|
||||
@onCenterChange={{action
|
||||
(mut this.calendarCenter)
|
||||
value="datetime"
|
||||
}}
|
||||
@onCenterChange={{fn this.handleCalenderCenterChange 0}}
|
||||
@onSelect={{this.handleSelectedDaysChange}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -28,14 +25,11 @@
|
|||
<InlineDatepicker
|
||||
@center={{this.calendarCenterNext}}
|
||||
@selectedDays={{this.selectedDays}}
|
||||
@onCenterChange={{action
|
||||
(mut this.calendarCenter)
|
||||
value="datetime"
|
||||
}}
|
||||
@onCenterChange={{fn this.handleCalenderCenterChange -1}}
|
||||
@onSelect={{this.handleSelectedDaysChange}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form.element>
|
||||
</FormElement>
|
||||
{{/let}}
|
|
@ -3,12 +3,13 @@ import { action } from '@ember/object';
|
|||
import { isArray } from '@ember/array';
|
||||
import { DateTime } from 'luxon';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import type { TrackedSet } from 'tracked-built-ins';
|
||||
import type { FormDataOption } from './create-options';
|
||||
import type BsFormElementComponent from 'ember-bootstrap/components/bs-form/element';
|
||||
|
||||
export interface CreateOptionsDatesSignature {
|
||||
Args: {
|
||||
options: TrackedSet<FormDataOption>;
|
||||
formElement: BsFormElementComponent;
|
||||
options: Array<FormDataOption>;
|
||||
updateOptions: (options: string[]) => void;
|
||||
};
|
||||
}
|
||||
|
@ -20,7 +21,7 @@ export default class CreateOptionsDates extends Component<CreateOptionsDatesSign
|
|||
: DateTime.local();
|
||||
|
||||
get selectedDays(): DateTime[] {
|
||||
return Array.from(this.args.options).map(
|
||||
return this.args.options.map(
|
||||
({ value }) => DateTime.fromISO(value) as DateTime,
|
||||
);
|
||||
}
|
||||
|
@ -45,4 +46,18 @@ export default class CreateOptionsDates extends Component<CreateOptionsDatesSign
|
|||
newDatesAsLuxonDateTime.map((datetime) => datetime.toISODate() as string),
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
handleCalenderCenterChange(
|
||||
offset: number,
|
||||
{ datetime }: { datetime: DateTime },
|
||||
) {
|
||||
this.calendarCenter = datetime.plus({ months: offset });
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
CreateOptionsDates: typeof CreateOptionsDates;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="cr-form-wrapper box">
|
||||
{{#if this.errorMessage}}
|
||||
<BsAlert type="warning">
|
||||
<BsAlert @type="warning">
|
||||
{{t this.errorMessage}}
|
||||
</BsAlert>
|
||||
{{/if}}
|
||||
|
@ -15,33 +15,15 @@
|
|||
>
|
||||
<div class="days">
|
||||
{{#each-in this.formData.datetimes as |date timeOptions|}}
|
||||
{{!--
|
||||
@glint-ignore
|
||||
Types for value returned by `{{#each-in}}` are broken if used
|
||||
with a `Map`. https://github.com/typed-ember/glint/issues/645
|
||||
--}}
|
||||
{{#each timeOptions as |timeOption indexInTimeOptions|}}
|
||||
{{!
|
||||
show summarized validation state for all times in a day
|
||||
}}
|
||||
|
||||
<div
|
||||
{{!
|
||||
TODO: daysValidationState is not defined!
|
||||
}}
|
||||
class={{if
|
||||
(get this.daysValidationState date)
|
||||
(concat "label-has-" (get this.daysValidationState date))
|
||||
"label-has-no-validation"
|
||||
}}
|
||||
data-test-day={{date}}
|
||||
>
|
||||
<div data-test-day={{date}}>
|
||||
<form.element
|
||||
{{!
|
||||
TODO: Simplify to dateStyle="full" after upgrading to Ember Intl v6
|
||||
}}
|
||||
@label={{format-date
|
||||
timeOption.jsDate
|
||||
weekday="long"
|
||||
day="numeric"
|
||||
month="long"
|
||||
year="numeric"
|
||||
}}
|
||||
@label={{format-date timeOption.jsDate dateStyle="full"}}
|
||||
{{!
|
||||
show label only for the first time of this date
|
||||
}}
|
||||
|
|
|
@ -102,6 +102,19 @@ class FormData {
|
|||
return this.datetimes.size > 1;
|
||||
}
|
||||
|
||||
get validationStatePerDate() {
|
||||
const validationState: Map<string, boolean> = new Map();
|
||||
|
||||
for (const [date, timeOptions] of this.datetimes.entries()) {
|
||||
validationState.set(
|
||||
date,
|
||||
Array.from(timeOptions).every((time) => time.isValid),
|
||||
);
|
||||
}
|
||||
|
||||
return validationState;
|
||||
}
|
||||
|
||||
@action
|
||||
addOption(date: string) {
|
||||
this.datetimes
|
||||
|
@ -215,7 +228,7 @@ export default class CreateOptionsDatetime extends Component<CreateOptoinsDateti
|
|||
|
||||
// validate input field for being partially filled
|
||||
@action
|
||||
validateInput(option: FormDataTimeOption, event: InputEvent) {
|
||||
validateInput(option: FormDataTimeOption, event: Event) {
|
||||
const element = event.target as HTMLInputElement;
|
||||
|
||||
// update partially filled time validation error
|
||||
|
@ -224,7 +237,7 @@ export default class CreateOptionsDatetime extends Component<CreateOptoinsDateti
|
|||
|
||||
// remove partially filled validation error if user fixed it
|
||||
@action
|
||||
updateInputValidation(option: FormDataTimeOption, event: InputEvent) {
|
||||
updateInputValidation(option: FormDataTimeOption, event: Event) {
|
||||
const element = event.target as HTMLInputElement;
|
||||
|
||||
if (element.checkValidity() && option.isPartiallyFilled) {
|
||||
|
@ -266,3 +279,9 @@ export default class CreateOptionsDatetime extends Component<CreateOptoinsDateti
|
|||
this.router.on('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
CreateOptionsDatetime: typeof CreateOptionsDatetime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{#let @form as |form|}}
|
||||
{{#let @formElement as |FormElement|}}
|
||||
{{#each @options as |option index|}}
|
||||
<form.element
|
||||
<FormElement
|
||||
{{! show label only on first item }}
|
||||
@label={{unless index (t "create.options.options.label")}}
|
||||
@model={{option}}
|
||||
|
@ -48,6 +48,6 @@
|
|||
></span>
|
||||
<span class="sr-only">{{t "create.options.button.add.label"}}</span>
|
||||
</BsButton>
|
||||
</form.element>
|
||||
</FormElement>
|
||||
{{/each}}
|
||||
{{/let}}
|
24
app/components/create-options-text.ts
Normal file
24
app/components/create-options-text.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
import type { FormDataOption } from './create-options';
|
||||
import type BsFormElementComponent from 'ember-bootstrap/components/bs-form/element';
|
||||
|
||||
interface CreateOptionsTextSignature {
|
||||
Args: {
|
||||
Named: {
|
||||
addOption: (value: string, afterPosition: number) => void;
|
||||
deleteOption: (option: FormDataOption) => void;
|
||||
formElement: BsFormElementComponent;
|
||||
options: FormDataOption[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const CreateOptionsText = templateOnlyComponent<CreateOptionsTextSignature>();
|
||||
|
||||
export default CreateOptionsText;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
CreateOptionsText: typeof CreateOptionsText;
|
||||
}
|
||||
}
|
|
@ -12,13 +12,13 @@
|
|||
@options={{this.formData.options}}
|
||||
@addOption={{this.formData.addOption}}
|
||||
@deleteOption={{this.formData.deleteOption}}
|
||||
@form={{form}}
|
||||
@formElement={{form.element}}
|
||||
/>
|
||||
{{else}}
|
||||
<CreateOptionsDates
|
||||
@options={{this.formData.options}}
|
||||
@updateOptions={{this.formData.updateOptions}}
|
||||
@form={{form}}
|
||||
@formElement={{form.element}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
|||
<NextButton />
|
||||
</div>
|
||||
<div class="col-6 col-md-4 order-1 text-right">
|
||||
<BackButton @onClick={{action "previousPage"}} />
|
||||
<BackButton @onClick={{this.previousPage}} />
|
||||
</div>
|
||||
</div>
|
||||
</BsForm>
|
||||
|
|
|
@ -104,7 +104,7 @@ export interface CreateOptionsSignature {
|
|||
};
|
||||
}
|
||||
|
||||
export default class CreateOptionsComponent extends Component<CreateOptionsSignature> {
|
||||
export default class CreateOptions extends Component<CreateOptionsSignature> {
|
||||
@service declare router: RouterService;
|
||||
|
||||
formData = new FormData(
|
||||
|
@ -136,3 +136,9 @@ export default class CreateOptionsComponent extends Component<CreateOptionsSigna
|
|||
this.router.on('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
CreateOptions: typeof CreateOptions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="ember-power-calendar-nav-control"
|
||||
onclick={{action calendar.actions.moveCenter -1 "month"}}
|
||||
{{on "click" (fn calendar.actions.moveCenter -1 "month")}}
|
||||
>
|
||||
«
|
||||
</button>
|
||||
|
@ -19,7 +19,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="ember-power-calendar-nav-control"
|
||||
onclick={{action calendar.actions.moveCenter 1 "month"}}
|
||||
{{on "click" (fn calendar.actions.moveCenter 1 "month")}}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
|
|
23
app/components/inline-datepicker.ts
Normal file
23
app/components/inline-datepicker.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
import type { DateTime } from 'luxon';
|
||||
|
||||
interface InlineDatepickerSignature {
|
||||
Args: {
|
||||
Named: {
|
||||
center: DateTime;
|
||||
onCenterChange: (day: { datetime: DateTime }) => void;
|
||||
onSelect: (days: { datetime: DateTime[] }) => void;
|
||||
selectedDays: DateTime[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const InlineDatepicker = templateOnlyComponent<InlineDatepickerSignature>();
|
||||
|
||||
export default InlineDatepicker;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
InlineDatepicker: typeof InlineDatepicker;
|
||||
}
|
||||
}
|
|
@ -30,3 +30,9 @@ export default class LanguageSelect extends Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
LanguageSelect: typeof LanguageSelect;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
13
app/components/loading-spinner.ts
Normal file
13
app/components/loading-spinner.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
|
||||
interface LoadingSpinnerSignature {}
|
||||
|
||||
const LoadingSpinner = templateOnlyComponent<LoadingSpinnerSignature>();
|
||||
|
||||
export default LoadingSpinner;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
LoadingSpinner: typeof LoadingSpinner;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<BsButton
|
||||
@buttonType="submit"
|
||||
@type="primary"
|
||||
class="cr-steps-bottom-nav__button cr-steps-bottom-nav__next-button next"
|
||||
type="submit"
|
||||
...attributes
|
||||
>
|
||||
<span class="cr-steps-bottom-nav__label">
|
||||
|
|
20
app/components/next-button.ts
Normal file
20
app/components/next-button.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
|
||||
interface NextButtonSignature {
|
||||
Args: {
|
||||
Named: {
|
||||
isPending?: boolean;
|
||||
};
|
||||
};
|
||||
Element: HTMLButtonElement;
|
||||
}
|
||||
|
||||
const NextButton = templateOnlyComponent<NextButtonSignature>();
|
||||
|
||||
export default NextButton;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
NextButton: typeof NextButton;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,12 @@
|
|||
{{! column for name }}
|
||||
</th>
|
||||
{{#each-in this.optionsPerDay as |jsDate count|}}
|
||||
{{!
|
||||
@glint-ignore
|
||||
We can be sure that count is a number because it is destructed from a
|
||||
Map, which values are only numbers. But somehow Glint / TypeScript
|
||||
is not sure about it.
|
||||
}}
|
||||
<th colspan={{count}}>
|
||||
{{!
|
||||
TODO: Simplify to dateStyle="full" after upgrading to Ember Intl v6
|
||||
|
@ -37,21 +43,23 @@
|
|||
{{#if (and @poll.isFindADate @poll.hasTimes)}}
|
||||
{{#if option.hasTime}}
|
||||
{{!
|
||||
TODO: Simplify to timeStyle="short" after upgrading to Ember Intl v6
|
||||
@glint-ignore
|
||||
Narrowring is not working here correctly. Due to the only executing if
|
||||
`option.hasTime` is `true`, we know that `option.jsDate` cannot be `null`.
|
||||
But TypeScript does not support narrowing through a chain of getters
|
||||
currently.
|
||||
}}
|
||||
{{format-date option.jsDate hour="numeric" minute="numeric"}}
|
||||
{{format-date option.jsDate timeStyle="short"}}
|
||||
{{/if}}
|
||||
{{else if @poll.isFindADate}}
|
||||
{{!
|
||||
TODO: Simplify to dateStyle="full" after upgrading to Ember Intl v6
|
||||
}}
|
||||
{{format-date
|
||||
option.jsDate
|
||||
weekday="long"
|
||||
day="numeric"
|
||||
month="long"
|
||||
year="numeric"
|
||||
@glint-ignore
|
||||
Narrowring is not working here correctly. Due to the only executing if
|
||||
`option.hasTime` is `true`, we know that `option.jsDate` cannot be `null`.
|
||||
But TypeScript does not support narrowing through a chain of getters
|
||||
currently.
|
||||
}}
|
||||
{{format-date option.jsDate dateStyle="full"}}
|
||||
{{else}}
|
||||
{{option.title}}
|
||||
{{/if}}
|
||||
|
@ -67,7 +75,7 @@
|
|||
{{user.name}}
|
||||
</td>
|
||||
{{#each @poll.options as |option index|}}
|
||||
{{#let (object-at index user.selections) as |selection|}}
|
||||
{{#let (get user.selections index) as |selection|}}
|
||||
<td
|
||||
class={{selection.type}}
|
||||
data-test-is-selection-cell
|
||||
|
|
|
@ -12,11 +12,19 @@ export default class PollEvaluationParticipantsTable extends Component<PollEvalu
|
|||
get optionsPerDay() {
|
||||
const { poll } = this.args;
|
||||
|
||||
const optionsPerDay = new Map();
|
||||
const optionsPerDay: Map<string, number> = new Map();
|
||||
for (const option of poll.options) {
|
||||
if (!option.day) {
|
||||
throw new Error(
|
||||
`Excepts all options to have a valid ISO8601 date string when using optionsPerDay getter`,
|
||||
);
|
||||
}
|
||||
|
||||
optionsPerDay.set(
|
||||
option.day,
|
||||
optionsPerDay.has(option.day) ? optionsPerDay.get(option.day) + 1 : 0,
|
||||
optionsPerDay.has(option.day)
|
||||
? (optionsPerDay.get(option.day) as number) + 1
|
||||
: 0,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -35,3 +43,9 @@ export default class PollEvaluationParticipantsTable extends Component<PollEvalu
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
PollEvaluationParticipantsTable: typeof PollEvaluationParticipantsTable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,24 +2,20 @@
|
|||
There must not be a line break between option text and "</strong>." cause otherwise
|
||||
we will see a space between option string and following dot.
|
||||
}}
|
||||
{{#if @isFindADate}}
|
||||
{{! Need to disable block indentation rule cause there shouldn't be a space between date and dot }}
|
||||
{{! template-lint-disable block-indentation }}
|
||||
{{!
|
||||
Checking `@evaluationBestOption.option.jsDate` is the same as checking `@isFindADate`.
|
||||
If poll type is `FindADate` we can be sure that every option is a valid ISO861
|
||||
string. Therefore `Option.jsDate` must be `true` by design. But Glint / TypeScript
|
||||
does not understand that. Therefore we need to use the less readable form.
|
||||
}}
|
||||
{{#if @evaluationBestOption.option.jsDate}}
|
||||
<strong data-test-best-option={{@evaluationBestOption.option.title}}>
|
||||
{{!
|
||||
TODO: Simplify to dateStyle="full" and timeStyle="short" after upgrading to Ember Intl v6
|
||||
}}
|
||||
{{format-date
|
||||
@evaluationBestOption.option.jsDate
|
||||
weekday="long"
|
||||
day="numeric"
|
||||
month="long"
|
||||
year="numeric"
|
||||
hour=(if @evaluationBestOption.option.hasTime "numeric" undefined)
|
||||
minute=(if @evaluationBestOption.option.hasTime "numeric" undefined)
|
||||
dateStyle="full"
|
||||
timeStyle=(if @evaluationBestOption.option.hasTime "short" undefined)
|
||||
timeZone=(if @timeZone @timeZone undefined)
|
||||
}}</strong>.
|
||||
{{! template-lint-enable block-indentation }}
|
||||
{{else}}
|
||||
<strong
|
||||
data-test-best-option={{@evaluationBestOption.option.title}}
|
||||
|
|
24
app/components/poll-evaluation-summary-option.ts
Normal file
24
app/components/poll-evaluation-summary-option.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
import type { BestOption } from './poll-evaluation-summary';
|
||||
|
||||
interface PollEvaluationSummaryOptionSignature {
|
||||
Args: {
|
||||
Named: {
|
||||
evaluationBestOption: BestOption;
|
||||
isFindADate: boolean;
|
||||
timeZone: string | null | undefined;
|
||||
};
|
||||
};
|
||||
Element: HTMLButtonElement;
|
||||
}
|
||||
|
||||
const PollEvaluationSummaryOption =
|
||||
templateOnlyComponent<PollEvaluationSummaryOptionSignature>();
|
||||
|
||||
export default PollEvaluationSummaryOption;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
PollEvaluationSummaryOption: typeof PollEvaluationSummaryOption;
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (get this.bestOptions.length 1)}}
|
||||
{{#if (gt this.bestOptions.length 1)}}
|
||||
<ul>
|
||||
{{#each this.bestOptions as |evaluationBestOption|}}
|
||||
<li>
|
||||
|
@ -34,6 +34,11 @@
|
|||
</ul>
|
||||
{{else}}
|
||||
<PollEvaluationSummaryOption
|
||||
{{!
|
||||
@glint-ignore
|
||||
We can be sure that `this.bestOptions` contains at least one item
|
||||
as a poll must always have at least one option.
|
||||
}}
|
||||
@evaluationBestOption={{get this.bestOptions 0}}
|
||||
@isFindADate={{@poll.isFindADate}}
|
||||
@timeZone={{@timeZone}}
|
||||
|
@ -42,9 +47,16 @@
|
|||
</p>
|
||||
|
||||
<p class="last-participation">
|
||||
{{#if this.lastParticipationAt}}
|
||||
{{t
|
||||
"poll.evaluation.lastParticipation"
|
||||
ago=(format-date-relative this.lastParticipationAt)
|
||||
}}
|
||||
{{else}}
|
||||
{{!
|
||||
No need for the else block as user cannot enter evaluation page if
|
||||
no one participated in the poll yet.
|
||||
}}
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
|
@ -9,49 +9,67 @@ import type Poll from 'croodle/models/poll';
|
|||
export interface PollEvaluationSummarySignature {
|
||||
Args: {
|
||||
poll: Poll;
|
||||
timeZone: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BestOption {
|
||||
answers: Record<'yes' | 'no' | 'maybe', number>;
|
||||
option: Option;
|
||||
score: number;
|
||||
}
|
||||
|
||||
export default class PollEvaluationSummary extends Component<PollEvaluationSummarySignature> {
|
||||
@service declare intl: IntlService;
|
||||
|
||||
get bestOptions() {
|
||||
get bestOptions(): BestOption[] | null {
|
||||
const { poll } = this.args;
|
||||
const { isFreeText, options, users } = poll;
|
||||
|
||||
// can not evaluate answer type free text
|
||||
if (isFreeText) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
// can not evaluate a poll without users
|
||||
if (users.length < 1) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
const answers = poll.answers.reduce(
|
||||
(answers: Record<string, number>, answer: Answer) => {
|
||||
(answers, answer: Answer) => {
|
||||
answers[answer.type] = 0;
|
||||
return answers;
|
||||
},
|
||||
{},
|
||||
{} as Record<'yes' | 'no' | 'maybe', number>,
|
||||
);
|
||||
const evaluation: {
|
||||
answers: Record<string, number>;
|
||||
option: Option;
|
||||
score: number;
|
||||
}[] = options.map((option: Option) => {
|
||||
const evaluation: BestOption[] = options.map((option: Option) => {
|
||||
return {
|
||||
answers: { ...answers },
|
||||
option,
|
||||
score: 0,
|
||||
};
|
||||
});
|
||||
const bestOptions = [];
|
||||
|
||||
users.forEach((user: User) => {
|
||||
user.selections.forEach(({ type }, i) => {
|
||||
evaluation[i]!.answers[type]++;
|
||||
if (!type) {
|
||||
// type may be undefined if poll does not force an answer to all options
|
||||
return;
|
||||
}
|
||||
|
||||
const evaluationForOption = evaluation[i];
|
||||
if (evaluationForOption === undefined) {
|
||||
throw new Error(
|
||||
'Mismatch between number of options in poll and selections for user',
|
||||
);
|
||||
}
|
||||
if (type !== 'yes' && type !== 'no' && type !== 'maybe') {
|
||||
throw new Error(
|
||||
`Encountered not supported type of user selection: ${type}`,
|
||||
);
|
||||
}
|
||||
evaluationForOption.answers[type]++;
|
||||
|
||||
switch (type) {
|
||||
case 'yes':
|
||||
|
@ -71,10 +89,11 @@ export default class PollEvaluationSummary extends Component<PollEvaluationSumma
|
|||
|
||||
evaluation.sort((a, b) => b.score - a.score);
|
||||
|
||||
const bestOptions = [];
|
||||
const bestScore = evaluation[0]!.score;
|
||||
for (let i = 0; i < evaluation.length; i++) {
|
||||
if (bestScore === evaluation[i]!.score) {
|
||||
bestOptions.push(evaluation[i]);
|
||||
for (const evaluationForOption of evaluation) {
|
||||
if (evaluationForOption.score === bestScore) {
|
||||
bestOptions.push(evaluationForOption);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -97,3 +116,9 @@ export default class PollEvaluationSummary extends Component<PollEvaluationSumma
|
|||
return lastParticipationAt;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
PollEvaluationSummary: typeof PollEvaluationSummary;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<BsButton
|
||||
@buttonType="submit"
|
||||
@type="primary"
|
||||
class="cr-steps-bottom-nav__button cr-steps-bottom-nav__next-button next"
|
||||
type="submit"
|
||||
...attributes
|
||||
>
|
||||
<span class="cr-steps-bottom-nav__label">
|
||||
|
|
20
app/components/save-button.ts
Normal file
20
app/components/save-button.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import templateOnlyComponent from '@ember/component/template-only';
|
||||
|
||||
interface SaveButtonSignature {
|
||||
Args: {
|
||||
Named: {
|
||||
isPending: boolean;
|
||||
};
|
||||
};
|
||||
Element: HTMLButtonElement;
|
||||
}
|
||||
|
||||
const SaveButton = templateOnlyComponent<SaveButtonSignature>();
|
||||
|
||||
export default SaveButton;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
SaveButton: typeof SaveButton;
|
||||
}
|
||||
}
|
|
@ -85,4 +85,9 @@ export default class PollController extends Controller {
|
|||
this.shouldUseLocalTimezone = true;
|
||||
this.timezoneChoosen = true;
|
||||
}
|
||||
|
||||
@action
|
||||
usePollTimezone() {
|
||||
this.timezoneChoosen = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,19 +9,27 @@ export interface FormatDateRelativeHelperSignature {
|
|||
Args: {
|
||||
Positional: Positional;
|
||||
};
|
||||
Return: string;
|
||||
}
|
||||
|
||||
export default class FormatDateRelativeHelper extends Helper {
|
||||
export default class FormatDateRelative extends Helper<FormatDateRelativeHelperSignature> {
|
||||
@service declare intl: IntlService;
|
||||
|
||||
compute([date]: Positional) {
|
||||
if (date instanceof Date) {
|
||||
date = date.toISOString();
|
||||
}
|
||||
compute([dateOrIsoString]: Positional) {
|
||||
const isoString =
|
||||
dateOrIsoString instanceof Date
|
||||
? dateOrIsoString.toISOString()
|
||||
: dateOrIsoString;
|
||||
|
||||
return DateTime.fromISO(date).toRelative({
|
||||
return DateTime.fromISO(isoString).toRelative({
|
||||
locale: this.intl.primaryLocale,
|
||||
padding: 1000,
|
||||
});
|
||||
})!;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
'format-date-relative': typeof FormatDateRelative;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
import { htmlSafe } from '@ember/template';
|
||||
|
||||
export default helper(function markAsSafeHtml([html]) {
|
||||
return htmlSafe(html);
|
||||
});
|
|
@ -9,8 +9,14 @@ export interface MarkAsSafeHtmlHelperSignature {
|
|||
};
|
||||
}
|
||||
|
||||
export default helper<MarkAsSafeHtmlHelperSignature>(function markAsSafeHtml([
|
||||
html,
|
||||
]) {
|
||||
const markAsSafeHtml = helper<MarkAsSafeHtmlHelperSignature>(([html]) => {
|
||||
return htmlSafe(html);
|
||||
});
|
||||
|
||||
export default markAsSafeHtml;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
'mark-as-safe-html': typeof markAsSafeHtml;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ function elementIsNotVisible(element: Element) {
|
|||
);
|
||||
}
|
||||
|
||||
export function scrollFirstInvalidElementIntoViewPort() {
|
||||
const scrollFirstInvalidElementIntoViewPort = helper(() => {
|
||||
return () => {
|
||||
// `schedule('afterRender', function() {})` would be more approperiate but there seems to be a
|
||||
// timing issue in Firefox causing the Browser not scrolling up far enough if doing so
|
||||
// delaying to next runloop therefore
|
||||
|
@ -71,8 +72,13 @@ export function scrollFirstInvalidElementIntoViewPort() {
|
|||
scrollTarget.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default helper(function () {
|
||||
return scrollFirstInvalidElementIntoViewPort;
|
||||
};
|
||||
});
|
||||
|
||||
export default scrollFirstInvalidElementIntoViewPort;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
'scroll-first-invalid-element-into-view-port': typeof scrollFirstInvalidElementIntoViewPort;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class Option {
|
|||
}
|
||||
|
||||
get day() {
|
||||
if (!this.datetime) {
|
||||
if (this.datetime === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ export default class Option {
|
|||
}
|
||||
|
||||
get jsDate() {
|
||||
if (!this.datetime) {
|
||||
if (this.datetime === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import Modifier from 'ember-modifier';
|
||||
|
||||
type Named = {
|
||||
enabled: boolean;
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
interface AutofocusModifierSignature {
|
||||
Element: HTMLInputElement;
|
||||
Element: HTMLInputElement | HTMLSelectElement;
|
||||
Args: {
|
||||
Named: Named;
|
||||
};
|
||||
|
@ -29,3 +29,9 @@ export default class AutofocusModifier extends Modifier<AutofocusModifierSignatu
|
|||
element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
autofocus: typeof AutofocusModifier;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
{{page-title 'Croodle'}}
|
||||
{{page-title "Croodle"}}
|
||||
|
||||
<nav class='cr-navbar navbar navbar-dark'>
|
||||
<h1 class='cr-logo'>
|
||||
<LinkTo @route='index' class='navbar-brand'>
|
||||
<nav class="cr-navbar navbar navbar-dark">
|
||||
<h1 class="cr-logo">
|
||||
<LinkTo @route="index" class="navbar-brand">
|
||||
Croodle
|
||||
</LinkTo>
|
||||
</h1>
|
||||
<div class='collapse' id='headerNavbar'>
|
||||
<form class='form-inline my-2 my-lg-0'>
|
||||
<LanguageSelect class='custom-select custom-select-sm' />
|
||||
<div class="collapse" id="headerNavbar">
|
||||
<form class="form-inline my-2 my-lg-0">
|
||||
<LanguageSelect />
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class='container cr-main'>
|
||||
<div id='messages'>
|
||||
<main class="container cr-main">
|
||||
<div id="messages">
|
||||
{{#each this.flashMessages.queue as |flash|}}
|
||||
<FlashMessage @flash={{flash}}>
|
||||
{{t flash.message}}
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
{{page-title (t "create.title")}}
|
||||
|
||||
<BsButtonGroup @justified={{true}} class="cr-steps-top-nav form-steps">
|
||||
{{#each this.formSteps as |formStep|}}
|
||||
{{#unless formStep.hidden}}
|
||||
<BsButton
|
||||
@onClick={{fn this.transitionTo formStep.route}}
|
||||
@type={{if
|
||||
(eq this.router.currentRouteName formStep.route)
|
||||
"primary is-active"
|
||||
"default"
|
||||
}}
|
||||
disabled={{formStep.disabled}}
|
||||
class="cr-steps-top-nav__button"
|
||||
data-test-form-step={{formStep.route}}
|
||||
>
|
||||
{{t formStep.label}}
|
||||
</BsButton>
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
<BsButton
|
||||
@onClick={{fn this.transitionTo "create.index"}}
|
||||
@type={{if
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<CreateOptions
|
||||
@isFindADate={{eq @model.pollType "FindADate"}}
|
||||
@isMakeAPoll={{eq @model.pollType "MakeAPoll"}}
|
||||
@options={{if
|
||||
(eq @model.pollType "FindADate")
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{{!--
|
||||
Add content you wish automatically added to the documents head
|
||||
here. The 'model' available in this template can be populated by
|
||||
setting values on the 'head-data' service.
|
||||
--}}
|
||||
<title>{{this.model.title}}</title>
|
|
@ -34,7 +34,13 @@
|
|||
<div class="col-lg-5 offset-lg-1">
|
||||
<h3>{{t "index.hoster.title"}}</h3>
|
||||
<p>
|
||||
{{t "index.hoster.text" gitHubLink=(mark-as-safe-html "<a href=\"https://github.com/jelhan/croodle\">GitHub</a>") htmlSafe=true}}
|
||||
{{t
|
||||
"index.hoster.text"
|
||||
gitHubLink=(mark-as-safe-html
|
||||
'<a href="https://github.com/jelhan/croodle">GitHub</a>'
|
||||
)
|
||||
htmlSafe=true
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
@onError={{fn this.linkAction "selected"}}
|
||||
@onSuccess={{fn this.linkAction "copied"}}
|
||||
class="btn btn-secondary cr-poll-link__copy-btn btn-sm"
|
||||
data-test-button="copy-link"
|
||||
>
|
||||
{{t "poll.link.copy-label"}}
|
||||
<span
|
||||
|
@ -145,7 +146,7 @@
|
|||
{{t "poll.modal.timezoneDiffers.button.useLocalTimezone"}}
|
||||
</BsButton>
|
||||
<BsButton
|
||||
@onClick={{action (mut this.timezoneChoosen) true}}
|
||||
@onClick={{this.usePollTimezone}}
|
||||
data-test-button="use-poll-timezone"
|
||||
>
|
||||
{{t "poll.modal.timezoneDiffers.button.usePollTimezone"}}
|
||||
|
|
|
@ -24,10 +24,7 @@
|
|||
{{#each @model.poll.options as |option index|}}
|
||||
{{#let
|
||||
(if
|
||||
(eq
|
||||
option.day
|
||||
(get (object-at (sub index 1) @model.poll.options) "day")
|
||||
)
|
||||
(eq option.day (get (get @model.poll.options (sub index 1)) "day"))
|
||||
false
|
||||
true
|
||||
)
|
||||
|
@ -57,7 +54,7 @@
|
|||
)
|
||||
option.title
|
||||
}}
|
||||
@model={{object-at index @model.formData.selections}}
|
||||
@model={{get @model.formData.selections index}}
|
||||
@property="value"
|
||||
data-test-form-element={{concat "option-" option.title}}
|
||||
/>
|
||||
|
@ -84,7 +81,7 @@
|
|||
)
|
||||
option.title
|
||||
}}
|
||||
@model={{object-at index @model.formData.selections}}
|
||||
@model={{get @model.formData.selections index}}
|
||||
@property="value"
|
||||
@showValidationOn="change"
|
||||
@useIcons={{false}}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { assert } from '@ember/debug';
|
|||
export type Answer = {
|
||||
labelTranslation: string;
|
||||
icon: string;
|
||||
type: string;
|
||||
type: 'yes' | 'no' | 'maybe';
|
||||
};
|
||||
|
||||
export default function (
|
||||
|
|
|
@ -34,7 +34,7 @@ module.exports = function (defaults) {
|
|||
enableTypeScriptTransform: true,
|
||||
},
|
||||
'ember-composable-helpers': {
|
||||
only: ['array', 'object-at', 'pick'],
|
||||
only: ['array', 'pick'],
|
||||
},
|
||||
'ember-math-helpers': {
|
||||
only: ['lte', 'sub'],
|
||||
|
|
132
package-lock.json
generated
132
package-lock.json
generated
|
@ -15,8 +15,9 @@
|
|||
"@ember/test-helpers": "^3.2.0",
|
||||
"@glimmer/component": "^1.1.2",
|
||||
"@glimmer/tracking": "^1.1.2",
|
||||
"@glint/environment-ember-loose": "^1.1.0",
|
||||
"@glint/template": "^1.1.0",
|
||||
"@glint/core": "^1.2.1",
|
||||
"@glint/environment-ember-loose": "^1.2.1",
|
||||
"@glint/template": "^1.2.1",
|
||||
"@release-it-plugins/lerna-changelog": "^6.0.0",
|
||||
"@tsconfig/ember": "^3.0.1",
|
||||
"@types/luxon": "^3.3.3",
|
||||
|
@ -8913,6 +8914,84 @@
|
|||
"@simple-dom/interface": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@glint/core": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@glint/core/-/core-1.2.1.tgz",
|
||||
"integrity": "sha512-25Zn65aLSN1M7s0D950sTNElZYRqa6HFA0xcT03iI/vQd1F6c3luMAXbFrsTSHlktZx2dqJ38c2dUnZJQBQgMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@glimmer/syntax": "^0.84.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"semver": "^7.5.2",
|
||||
"silent-error": "^1.1.1",
|
||||
"uuid": "^8.3.2",
|
||||
"vscode-languageserver": "^8.0.1",
|
||||
"vscode-languageserver-textdocument": "^1.0.5",
|
||||
"vscode-uri": "^3.0.8",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"bin": {
|
||||
"glint": "bin/glint.js",
|
||||
"glint-language-server": "bin/glint-language-server.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@glint/core/node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@glint/core/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@glint/core/node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@glint/core/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@glint/core/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@glint/environment-ember-loose": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@glint/environment-ember-loose/-/environment-ember-loose-1.2.1.tgz",
|
||||
|
@ -56210,6 +56289,55 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz",
|
||||
"integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.1.0.tgz",
|
||||
"integrity": "sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"vscode-languageserver-protocol": "3.17.3"
|
||||
},
|
||||
"bin": {
|
||||
"installServerIntoExtension": "bin/installServerIntoExtension"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-protocol": {
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz",
|
||||
"integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.1.0",
|
||||
"vscode-languageserver-types": "3.17.3"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-textdocument": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz",
|
||||
"integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
|
||||
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vscode-uri": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
|
||||
"integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/walk-sync": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.4.tgz",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"lint:hbs:fix": "ember-template-lint . --fix",
|
||||
"lint:js": "eslint . --cache",
|
||||
"lint:js:fix": "eslint . --fix",
|
||||
"lint:types": "tsc --noEmit",
|
||||
"lint:types": "glint",
|
||||
"release": "release-it",
|
||||
"start": "ember serve",
|
||||
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
|
||||
|
@ -34,8 +34,9 @@
|
|||
"@ember/test-helpers": "^3.2.0",
|
||||
"@glimmer/component": "^1.1.2",
|
||||
"@glimmer/tracking": "^1.1.2",
|
||||
"@glint/environment-ember-loose": "^1.1.0",
|
||||
"@glint/template": "^1.1.0",
|
||||
"@glint/core": "^1.2.1",
|
||||
"@glint/environment-ember-loose": "^1.2.1",
|
||||
"@glint/template": "^1.2.1",
|
||||
"@release-it-plugins/lerna-changelog": "^6.0.0",
|
||||
"@tsconfig/ember": "^3.0.1",
|
||||
"@types/luxon": "^3.3.3",
|
||||
|
|
|
@ -10,8 +10,6 @@ import { setupApplicationTest } from 'ember-qunit';
|
|||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import pageParticipation from 'croodle/tests/pages/poll/participation';
|
||||
import { DateTime } from 'luxon';
|
||||
import { triggerCopySuccess } from 'ember-cli-clipboard/test-support';
|
||||
|
||||
module('Acceptance | view poll', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.setItem('locale', 'en');
|
||||
|
@ -32,7 +30,7 @@ module('Acceptance | view poll', function (hooks) {
|
|||
'share link is shown',
|
||||
);
|
||||
|
||||
await triggerCopySuccess();
|
||||
await click('.copy-btn');
|
||||
/*
|
||||
* Can't test if link is actually copied to clipboard due to api
|
||||
* restrictions. Due to security it's not allowed to read from clipboard.
|
||||
|
|
|
@ -13,5 +13,8 @@
|
|||
],
|
||||
"*": ["types/*"]
|
||||
}
|
||||
},
|
||||
"glint": {
|
||||
"environment": "ember-loose"
|
||||
}
|
||||
}
|
||||
|
|
17
types/ember-bootstrap/components/bs-alert.d.ts
vendored
Normal file
17
types/ember-bootstrap/components/bs-alert.d.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
BsAlert: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
type: string;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
Element: HTMLDivElement;
|
||||
}>;
|
||||
}
|
||||
}
|
17
types/ember-bootstrap/components/bs-button-group.d.ts
vendored
Normal file
17
types/ember-bootstrap/components/bs-button-group.d.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
BsButtonGroup: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
justified: boolean;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
Element: HTMLDivElement;
|
||||
}>;
|
||||
}
|
||||
}
|
19
types/ember-bootstrap/components/bs-button.d.ts
vendored
Normal file
19
types/ember-bootstrap/components/bs-button.d.ts
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
BsButton: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
onClick?: () => void;
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
type?: string;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
Element: HTMLButtonElement;
|
||||
}>;
|
||||
}
|
||||
}
|
30
types/ember-bootstrap/components/bs-form.d.ts
vendored
Normal file
30
types/ember-bootstrap/components/bs-form.d.ts
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
import type BsFormElementComponent from './bs-form/element';
|
||||
|
||||
type BsFormComponent = ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
formLayout: 'horizontal' | 'vertical';
|
||||
model: unknown;
|
||||
onInvalid?: () => void;
|
||||
onSubmit: () => void;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [
|
||||
{
|
||||
element: BsFormElementComponent;
|
||||
isSubmitting: boolean;
|
||||
},
|
||||
];
|
||||
};
|
||||
Element: HTMLDivElement;
|
||||
}>;
|
||||
|
||||
export default BsFormComponent;
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
BsForm: BsFormComponent;
|
||||
}
|
||||
}
|
34
types/ember-bootstrap/components/bs-form/element.d.ts
vendored
Normal file
34
types/ember-bootstrap/components/bs-form/element.d.ts
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
|
||||
type BsFormElementComponent = ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
controlType?: 'checkbox' | 'select' | 'text' | 'textarea' | 'time';
|
||||
invisibleLabel?: boolean;
|
||||
label?: string;
|
||||
model?: unknown;
|
||||
property?: string;
|
||||
showValidationOn?: string | string[];
|
||||
useIcons?: boolean;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [
|
||||
{
|
||||
control: ComponentLike<{
|
||||
Args: {
|
||||
Named: Record<string, unknown>;
|
||||
};
|
||||
Element: HTMLInputElement;
|
||||
}>;
|
||||
id: string;
|
||||
setValue: (value: unknown) => void;
|
||||
validation: 'success' | 'error' | 'warning' | null;
|
||||
value: unknown;
|
||||
},
|
||||
];
|
||||
};
|
||||
Element: HTMLDivElement;
|
||||
}>;
|
||||
|
||||
export default BsFormElementComponent;
|
35
types/ember-bootstrap/components/bs-modal.d.ts
vendored
Normal file
35
types/ember-bootstrap/components/bs-modal.d.ts
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
BsModal: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
autoClose: boolean;
|
||||
closeButton: boolean;
|
||||
footer: boolean;
|
||||
keyboard: boolean;
|
||||
open: boolean;
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [
|
||||
{
|
||||
body: ComponentLike<{
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
}>;
|
||||
footer: ComponentLike<{
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
}>;
|
||||
},
|
||||
];
|
||||
};
|
||||
Element: HTMLDivElement;
|
||||
}>;
|
||||
}
|
||||
}
|
19
types/ember-cli-clipboard/components/copy-button.d.ts
vendored
Normal file
19
types/ember-cli-clipboard/components/copy-button.d.ts
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
CopyButton: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
onError: () => void;
|
||||
onSuccess: () => void;
|
||||
text: string;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
Element: HTMLButtonElement;
|
||||
}>;
|
||||
}
|
||||
}
|
17
types/ember-cli-flash/components/flash-message.d.ts
vendored
Normal file
17
types/ember-cli-flash/components/flash-message.d.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
import type FlashObject from 'ember-cli-flash/flash/object';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
FlashMessage: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
flash: FlashObject;
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [];
|
||||
};
|
||||
}>;
|
||||
}
|
||||
}
|
1
types/ember-cli-flash/flash/object.d.ts
vendored
1
types/ember-cli-flash/flash/object.d.ts
vendored
|
@ -8,6 +8,7 @@ declare module 'ember-cli-flash/flash/object' {
|
|||
exitTimer: number;
|
||||
isExitable: boolean;
|
||||
initializedTime: number;
|
||||
message: string;
|
||||
destroyMessage(): void;
|
||||
exitMessage(): void;
|
||||
preventExit(): void;
|
||||
|
|
12
types/ember-composable-helpers/helpers/pick.d.ts
vendored
Normal file
12
types/ember-composable-helpers/helpers/pick.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { HelperLike } from '@glint/template';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
pick: HelperLike<{
|
||||
Args: {
|
||||
Positional: [path: string, action?: (_: unknown) => unknown];
|
||||
};
|
||||
Return: (_: unknown) => unknown;
|
||||
}>;
|
||||
}
|
||||
}
|
18
types/ember-intl/helpers/format-date.d.ts
vendored
Normal file
18
types/ember-intl/helpers/format-date.d.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { HelperLike } from '@glint/template';
|
||||
|
||||
// Ember Intl ships glint types. But as of today (October 29, 2023)
|
||||
// they are buggy and cannot be used.
|
||||
// Types provided by Ember Intl should be used instead as soon as
|
||||
// type issues have been fixed in the addon itself.
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
'format-date': HelperLike<{
|
||||
Args: {
|
||||
Positional: [Date | string];
|
||||
Named: Record<string, unknown>;
|
||||
};
|
||||
Return: string;
|
||||
}>;
|
||||
}
|
||||
}
|
18
types/ember-intl/helpers/t.d.ts
vendored
Normal file
18
types/ember-intl/helpers/t.d.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { HelperLike } from '@glint/template';
|
||||
|
||||
// Ember Intl ships glint types. But as of today (October 29, 2023)
|
||||
// they are buggy and cannot be used.
|
||||
// Types provided by Ember Intl should be used instead as soon as
|
||||
// type issues have been fixed in the addon itself.
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
t: HelperLike<{
|
||||
Args: {
|
||||
Positional: [string];
|
||||
Named: Record<string, unknown>;
|
||||
};
|
||||
Return: string;
|
||||
}>;
|
||||
}
|
||||
}
|
15
types/ember-page-title/helpers/page-title.d.ts
vendored
Normal file
15
types/ember-page-title/helpers/page-title.d.ts
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { HelperLike } from '@glint/template';
|
||||
|
||||
// Glint support in ember-page-title itself is tracked here:
|
||||
// https://github.com/ember-cli/ember-page-title/issues/239
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
'page-title': HelperLike<{
|
||||
Args: {
|
||||
Positional: [string];
|
||||
};
|
||||
Return: void;
|
||||
}>;
|
||||
}
|
||||
}
|
29
types/ember-power-calendar/components/power-calendar-mutliple.d.ts
vendored
Normal file
29
types/ember-power-calendar/components/power-calendar-mutliple.d.ts
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { ComponentLike } from '@glint/template';
|
||||
import type { DateTime } from 'luxon';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry {
|
||||
PowerCalendarMultiple: ComponentLike<{
|
||||
Args: {
|
||||
Named: {
|
||||
center: DateTime;
|
||||
onCenterChange: (day: { datetime: DateTime }) => void;
|
||||
onSelect: (days: { datetime: DateTime[] }) => void;
|
||||
selected: DateTime[];
|
||||
};
|
||||
};
|
||||
Blocks: {
|
||||
default: [
|
||||
{
|
||||
actions: {
|
||||
moveCenter: (step: number, unit: 'month') => void;
|
||||
};
|
||||
center: Date;
|
||||
Days: ComponentLike;
|
||||
},
|
||||
];
|
||||
};
|
||||
Element: HTMLDivElement;
|
||||
}>;
|
||||
}
|
||||
}
|
8
types/global.d.ts
vendored
8
types/global.d.ts
vendored
|
@ -1 +1,9 @@
|
|||
import '@glint/environment-ember-loose';
|
||||
import type EmberMathRegistry from 'ember-math-helpers/template-registry';
|
||||
import type EmberTruthRegistry from 'ember-truth-helpers/template-registry';
|
||||
|
||||
declare module '@glint/environment-ember-loose/registry' {
|
||||
export default interface Registry
|
||||
extends EmberMathRegistry,
|
||||
EmberTruthRegistry {}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue