diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f9071e9..1980f03e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,3 +79,32 @@ jobs: - name: Run tests run: pnpm ember try:one ${{ matrix.ember-try-scenario }} --skip-cleanup working-directory: test-app + + typescript-compatibility: + name: Type checking - ${{ matrix.typescript-scenario }} + runs-on: ubuntu-latest + + needs: [test] + + strategy: + fail-fast: false + matrix: + typescript-scenario: + - typescript@5.0 + - typescript@5.1 + - typescript@next + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install pnpm + uses: wyvox/action-setup-pnpm@v3 + with: + node-version: 16.x + args: "--frozen-lockfile" + - name: Update TS version + run: pnpm add -D ${{ matrix.typescript-scenario }} + working-directory: ember-amount-input + - name: Type checking + run: pnpm lint:types + working-directory: ember-amount-input diff --git a/README.md b/README.md index c6425425..8a4c8b72 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Easily create a money input with the currency of your liking. ## Compatibility - Ember.js v3.28 or above +- TypeScript v5.0 or above - Embroider or ember-auto-import v2 ## Installation diff --git a/ember-amount-input/rollup.config.mjs b/ember-amount-input/rollup.config.mjs index aa685186..a70f717f 100644 --- a/ember-amount-input/rollup.config.mjs +++ b/ember-amount-input/rollup.config.mjs @@ -18,7 +18,7 @@ export default { plugins: [ // These are the modules that users should be able to import from your // addon. Anything not listed here may get optimized away. - addon.publicEntrypoints(['components/**/*.js']), + addon.publicEntrypoints(['components/**/*.js', 'template-registry.js']), // These are the modules that should get reexported into the traditional // "app" tree. Things in here should also be in publicEntrypoints above, but diff --git a/ember-amount-input/src/components/amount-input.hbs b/ember-amount-input/src/components/amount-input.hbs index ccbc91f5..a2c43572 100644 --- a/ember-amount-input/src/components/amount-input.hbs +++ b/ember-amount-input/src/components/amount-input.hbs @@ -1,4 +1,3 @@ -{{! @glint-nocheck: not typesafe yet }}
{{this.currency}} diff --git a/ember-amount-input/src/components/amount-input.js b/ember-amount-input/src/components/amount-input.js deleted file mode 100644 index 96de3d7f..00000000 --- a/ember-amount-input/src/components/amount-input.js +++ /dev/null @@ -1,152 +0,0 @@ -import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import './amount-input.css'; - -const KEY_CODE_E = 69; -const KEY_CODE_FULLSTOP = 190; -const KEY_CODE_COMMA = 188; - -/** - A amount/money input component. Usage: - - ```hbs - - ``` - - @class AmountInput - @public -*/ -export default class AmountInput extends Component { - argOrDefault(arg, defaultValue) { - if (Object.keys(this.args).includes(arg)) { - return this.args[arg]; - } - return defaultValue; - } - - /** - The currency displayed in the input - @argument currency - @type String? - */ - get currency() { - return this.argOrDefault('currency', 'EUR'); - } - - /** - Disable the input - @argument disabled - @type Boolean? - */ - - /** - Set readonly the input - @argument readonly - @type Boolean? - */ - - /** - A custom class applied on the input - @argument inputClass - @type String? - */ - - /** - A custom id applied on the input - @argument inputId - @type String? - */ - get inputId() { - return this.argOrDefault('inputId', 'amount-input'); - } - - /** - Defines the argument sent to toFixed() - Can be n>=0 - @argument numberOfDecimal - @type Number? - */ - - get numberOfDecimal() { - return this.argOrDefault('numberOfDecimal', 2); - } - - /** - The placeholder - @argument placeholder - @type String? - */ - get placeholder() { - return this.argOrDefault('placeholder', '0.00'); - } - - /** - The min attribute specifies the minimum value for the amount element. - @argument min - @type Number - */ - - /** - The max attribute specifies the maximum value for the amount element. - @argument max - @type Number - */ - - /** - The input's value. - Should be updated using the `update()` argument - @argument value - @type Number - @required - */ - - /** - Specifies the number intervals for the input field - Some browser also use it to validate the value if it used in a form - Can be n>=0 - @argument step - @type Number? - */ - get step() { - return this.argOrDefault('step', 0.01); - } - - /** - Function triggered when onInput and onFocusOut with the new value - Use it to update your value - @argument update - @type function - @param Number value - @required - */ - - @action - onKeyDown(event) { - if (event.keyCode === KEY_CODE_E) { - event.preventDefault(); - return false; - } else if ( - this.numberOfDecimal === 0 && - [KEY_CODE_FULLSTOP, KEY_CODE_COMMA].includes(event.keyCode) - ) { - event.preventDefault(); - return false; - } - } - - @action - onInput(e) { - var { value } = e.target; - this.args.update?.(value); - return true; - } - - @action - onFocusOut(e) { - var { valueAsNumber, value } = e.target; - if (value) { - // add decimals - this.args.update?.(valueAsNumber.toFixed(this.numberOfDecimal)); - } - } -} diff --git a/ember-amount-input/src/components/amount-input.ts b/ember-amount-input/src/components/amount-input.ts new file mode 100644 index 00000000..976161ff --- /dev/null +++ b/ember-amount-input/src/components/amount-input.ts @@ -0,0 +1,180 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import './amount-input.css'; + +const KEY_CODE_E = 69; +const KEY_CODE_FULLSTOP = 190; +const KEY_CODE_COMMA = 188; + +export interface AmountInputArgs { + /** + * The currency displayed in the input + */ + currency?: string; + + /** + * Disables the input + */ + disabled?: boolean; + + /** + * A custom class applied on the input + */ + inputClass?: string; + + /** + * A custom id applied on the input + */ + inputId?: string; + + /** + * Specifies the minimum value for the input field + */ + min?: number; + + /** + * Specifies the maximum value for the input field + */ + max?: number; + + /** + * Specifies the number of decimals to use for the amount value + */ + numberOfDecimal?: number; + + /** + * The placeholder displayed in the input + */ + placeholder?: string; + + /** + * Specifies if the input field should be read-only + */ + readonly?: boolean; + + /** + * Specifies the number intervals for the input field + */ + step?: number; + + /** + * The callback function triggered when the input value is updated + */ + update: (value: string) => void; + + /** + * The value of the input. It should be updated using the `update` argument + */ + value: number | string; +} + +export interface AmountInputSignature { + Element: HTMLDivElement; + Args: AmountInputArgs; +} + +/** + * An amount/money input component. + * + * @public + * @class AmountInput + * + * @example + * + * ```hbs + * + * ``` + */ +export default class AmountInput extends Component { + /** + * The currency displayed in the input. + * Defaults to `EUR`. + */ + get currency(): string { + return this.argOrDefault('currency', 'EUR'); + } + + /** + * A custom id applied on the input. + * Defaults to `amount-input`. + */ + get inputId(): string { + return this.argOrDefault('inputId', 'amount-input'); + } + + /** + * Specifies the number of decimals to use for the amount value. + * Can be >= 0. + * Defaults to 2. + */ + get numberOfDecimal(): number { + return this.argOrDefault('numberOfDecimal', 2); + } + + /** + * The placeholder displayed in the input. + * Defaults to `0.00`. + */ + get placeholder(): string { + return this.argOrDefault('placeholder', '0.00'); + } + + /** + * Specifies the number intervals for the input field. + * Can be >= 0. + * Defaults to 0.01. + */ + get step(): number { + return this.argOrDefault('step', 0.01); + } + + @action + onKeyDown(event: KeyboardEvent): boolean { + if (event.keyCode === KEY_CODE_E) { + event.preventDefault(); + return false; + } else if ( + this.numberOfDecimal === 0 && + [KEY_CODE_FULLSTOP, KEY_CODE_COMMA].includes(event.keyCode) + ) { + event.preventDefault(); + return false; + } + + return true; + } + + @action + onInput(event: Event): boolean { + if (!(event.target instanceof HTMLInputElement)) return false; + + const { value } = event.target; + this.args.update(value); + + return true; + } + + @action + onFocusOut(event: FocusEvent): boolean { + if (event.target instanceof HTMLInputElement) { + const { valueAsNumber, value } = event.target; + if (value) { + this.args.update(valueAsNumber.toFixed(this.numberOfDecimal)); + return true; + } + } + + return false; + } + + private argOrDefault( + arg: K, + defaultValue: D, + ): T { + if (Object.keys(this.args).includes(arg)) { + return this.args[arg] as T; + } + + return defaultValue; + } +} diff --git a/ember-amount-input/src/template-registry.ts b/ember-amount-input/src/template-registry.ts new file mode 100644 index 00000000..6e442057 --- /dev/null +++ b/ember-amount-input/src/template-registry.ts @@ -0,0 +1,5 @@ +import type AmountInput from './components/amount-input'; + +export default interface Registry { + AmountInput: typeof AmountInput; +} diff --git a/ember-amount-input/tsconfig.json b/ember-amount-input/tsconfig.json index 4b3c5175..70db6ee5 100644 --- a/ember-amount-input/tsconfig.json +++ b/ember-amount-input/tsconfig.json @@ -8,7 +8,7 @@ "environment": "ember-loose" }, "compilerOptions": { - "allowJs": true, + "allowJs": false, "declarationDir": "declarations", "skipLibCheck": true, }