├── .stylelintrc.mjs ├── prettier.config.mjs ├── addon-main.cjs ├── .template-lintrc.cjs ├── .stylelintignore ├── demo ├── routes │ └── index.js ├── router.js ├── styles │ └── app.css ├── config.js ├── app.js └── templates │ ├── application.gjs │ ├── native.gjs │ └── modifier.gjs ├── .gitignore ├── src ├── utils │ └── focus.js └── modifiers │ └── auto-focus.js ├── .env.development ├── index.html ├── .editorconfig ├── .prettierignore ├── config └── ember-cli-update.json ├── babel.publish.config.cjs ├── CONTRIBUTING.md ├── vite.config.mjs ├── testem.cjs ├── tests ├── index.html ├── test-helper.js └── integration │ └── auto-focus-test.gjs ├── tsconfig.publish.json ├── LICENSE.md ├── babel.config.cjs ├── .github └── workflows │ └── ci.yml ├── .try.mjs ├── rollup.config.mjs ├── CHANGELOG.md ├── eslint.config.mjs ├── README.md └── package.json /.stylelintrc.mjs: -------------------------------------------------------------------------------- 1 | import zestia from '@zestia/stylelint-config'; 2 | 3 | export default zestia; 4 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | import zestia from '@zestia/prettier-config'; 2 | 3 | export default zestia; 4 | -------------------------------------------------------------------------------- /addon-main.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { addonV1Shim } = require('@embroider/addon-shim'); 4 | module.exports = addonV1Shim(__dirname); 5 | -------------------------------------------------------------------------------- /.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['@zestia/template-lint-config'], 5 | extends: 'zestia:recommended' 6 | }; 7 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | # unconventional files 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | /dist-tests/ 7 | 8 | # addons 9 | /.node_modules.ember-try/ 10 | -------------------------------------------------------------------------------- /demo/routes/index.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import { service } from '@ember/service'; 3 | 4 | export default class IndexRoute extends Route { 5 | @service router; 6 | 7 | redirect() { 8 | return this.router.transitionTo('modifier'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | dist/ 3 | dist-tests/ 4 | declarations/ 5 | 6 | # from scenarios 7 | tmp/ 8 | config/optional-features.json 9 | ember-cli-build.js 10 | 11 | # npm/pnpm/yarn pack output 12 | *.tgz 13 | 14 | # deps & caches 15 | node_modules/ 16 | .eslintcache 17 | .prettiercache 18 | -------------------------------------------------------------------------------- /src/utils/focus.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable ember/no-runloop */ 2 | 3 | import { next } from '@ember/runloop'; 4 | 5 | export default function focus(element, options) { 6 | element.dataset.programmaticallyFocused = 'true'; 7 | element.focus(options); 8 | next(() => delete element.dataset.programmaticallyFocused); 9 | } 10 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # This file is committed to git and should not contain any secrets. 2 | # 3 | # Vite recommends using .env.local or .env.[mode].local if you need to manage secrets 4 | # SEE: https://vite.dev/guide/env-and-mode.html#env-files for more information. 5 | 6 | 7 | # Default NODE_ENV with vite build --mode=test is production 8 | NODE_ENV=development 9 | -------------------------------------------------------------------------------- /demo/router.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable array-callback-return */ 2 | 3 | import EmberRouter from '@ember/routing/router'; 4 | import config from './config.js'; 5 | 6 | export default class Router extends EmberRouter { 7 | location = config.locationType; 8 | rootURL = config.rootURL; 9 | } 10 | 11 | Router.map(function () { 12 | this.route('native'); 13 | this.route('modifier'); 14 | }); 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
31 |
32 |
33 | );
34 |
--------------------------------------------------------------------------------
/tsconfig.publish.json:
--------------------------------------------------------------------------------
1 | /**
2 | * This tsconfig is only used for publishing.
3 | *
4 | * For local dev experience, see the tsconfig.json
5 | */
6 | {
7 | "extends": "@ember/library-tsconfig",
8 | "include": ["./src/**/*", "./unpublished-development-types/**/*"],
9 | "glint": {
10 | "environment": ["ember-loose", "ember-template-imports"]
11 | },
12 | "compilerOptions": {
13 | "allowJs": true,
14 | "declarationDir": "declarations",
15 |
16 | /**
17 | https://www.typescriptlang.org/tsconfig#rootDir
18 | "Default: The longest common path of all non-declaration input files."
19 |
20 | Because we want our declarations' structure to match our rollup output,
21 | we need this "rootDir" to match the "srcDir" in the rollup.config.mjs.
22 |
23 | This way, we can have simpler `package.json#exports` that matches
24 | imports to files on disk
25 | */
26 | "rootDir": "./src",
27 |
28 | "types": ["ember-source/types"]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/modifiers/auto-focus.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable ember/no-runloop */
2 |
3 | import Modifier from 'ember-modifier';
4 | import focus from '@zestia/ember-auto-focus/utils/focus';
5 | import { scheduleOnce } from '@ember/runloop';
6 |
7 | export default class AutoFocusModifier extends Modifier {
8 | didSetup = false;
9 |
10 | modify(element, positional, named) {
11 | if (this.didSetup) {
12 | return;
13 | }
14 |
15 | this.didSetup = true;
16 |
17 | const { disabled } = named;
18 |
19 | if (disabled) {
20 | return;
21 | }
22 |
23 | const [selector] = positional;
24 |
25 | if (selector) {
26 | element = element.querySelector(selector);
27 | }
28 |
29 | if (!element) {
30 | return;
31 | }
32 |
33 | scheduleOnce('afterRender', this, afterRender, element, named);
34 | }
35 | }
36 |
37 | function afterRender(element, options) {
38 | if (element.contains(document.activeElement)) {
39 | return;
40 | }
41 |
42 | focus(element, options);
43 | }
44 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable array-callback-return */
2 |
3 | import EmberApp from '@ember/application';
4 | import Resolver from 'ember-resolver';
5 | import EmberRouter from '@ember/routing/router';
6 | import * as QUnit from 'qunit';
7 | import { setApplication } from '@ember/test-helpers';
8 | import { setup } from 'qunit-dom';
9 | import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit';
10 |
11 | class Router extends EmberRouter {
12 | location = 'none';
13 | rootURL = '/';
14 | }
15 |
16 | class TestApp extends EmberApp {
17 | modulePrefix = 'test-app';
18 | Resolver = Resolver.withModules({
19 | 'test-app/router': { default: Router }
20 | // add any custom services here
21 | });
22 | }
23 |
24 | Router.map(function () {});
25 |
26 | export function start() {
27 | setApplication(
28 | TestApp.create({
29 | autoboot: false,
30 | rootElement: '#ember-testing'
31 | })
32 | );
33 | setup(QUnit.assert);
34 | setupEmberOnerrorValidation();
35 | qunitStart();
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2025
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/babel.config.cjs:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * This babel.config is not used for publishing.
5 | * It's only for the local editing experience
6 | * (and linting)
7 | */
8 | const { buildMacros } = require('@embroider/macros/babel');
9 |
10 | const {
11 | babelCompatSupport,
12 | templateCompatSupport
13 | } = require('@embroider/compat/babel');
14 |
15 | const macros = buildMacros();
16 |
17 | // For scenario testing
18 | const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD);
19 |
20 | module.exports = {
21 | plugins: [
22 | [
23 | 'babel-plugin-ember-template-compilation',
24 | {
25 | transforms: [
26 | ...(isCompat ? templateCompatSupport() : macros.templateMacros)
27 | ]
28 | }
29 | ],
30 | [
31 | 'module:decorator-transforms',
32 | {
33 | runtime: {
34 | import: require.resolve('decorator-transforms/runtime-esm')
35 | }
36 | }
37 | ],
38 | ...(isCompat ? babelCompatSupport() : macros.babelMacros)
39 | ],
40 |
41 | generatorOpts: {
42 | compact: false
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/demo/templates/native.gjs:
--------------------------------------------------------------------------------
1 | import Route from 'ember-route-template';
2 | import { on } from '@ember/modifier';
3 | import Component from '@glimmer/component';
4 | import { tracked } from '@glimmer/tracking';
5 |
6 | class NativeRoute extends Component {
7 | @tracked shouldShowInput;
8 |
9 | showInput = () => (this.shouldShowInput = true);
10 | hideInput = () => (this.shouldShowInput = false);
11 |
12 |
13 | {{#if this.shouldShowInput}}
14 | 15 | {{! template-lint-disable no-autofocus-attribute }} 16 | 17 |
18 | {{/if}} 19 | 20 |
21 | <input autofocus>
22 |
23 |
24 | 25 | Notice that on the second render, the input 26 | is not 27 | focused. 28 |
29 | 30 |31 | 34 | 37 |
38 | 39 | } 40 | 41 | export default Route(NativeRoute); 42 | -------------------------------------------------------------------------------- /demo/templates/modifier.gjs: -------------------------------------------------------------------------------- 1 | import Route from 'ember-route-template'; 2 | import { on } from '@ember/modifier'; 3 | import Component from '@glimmer/component'; 4 | import { tracked } from '@glimmer/tracking'; 5 | import autoFocus from '@zestia/ember-auto-focus/modifiers/auto-focus'; 6 | 7 | class ModifierRoute extends Component { 8 | @tracked shouldShowInput; 9 | 10 | showInput = () => (this.shouldShowInput = true); 11 | hideInput = () => (this.shouldShowInput = false); 12 | 13 | 14 | {{#if this.shouldShowInput}} 15 |16 | 17 |
18 | {{/if}} 19 | 20 | {{! prettier-ignore }} 21 |
22 | <input \{{autoFocus}}>
23 |
24 |
25 | 26 | Notice that on the second render, the input 27 | 28 | is 29 | 30 | focused. 31 |
32 | 33 |34 | 37 | 40 |
41 | 42 | } 43 | 44 | export default Route(ModifierRoute); 45 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: {} 9 | 10 | concurrency: 11 | group: ci-${{ github.head_ref || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | name: 'Tests' 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 10 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Install Node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 18 26 | cache: npm 27 | - name: Install Dependencies 28 | run: npm ci 29 | - name: Lint 30 | run: npm run lint 31 | - name: Run Tests 32 | run: npm run test:ember 33 | 34 | floating: 35 | name: 'Floating Dependencies' 36 | runs-on: ubuntu-latest 37 | timeout-minutes: 10 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | - uses: actions/setup-node@v3 42 | with: 43 | node-version: 18 44 | cache: npm 45 | - name: Install Dependencies 46 | run: npm install --no-shrinkwrap 47 | - name: Run Tests 48 | run: npm run test:ember 49 | 50 | try-scenarios: 51 | name: ${{ matrix.try-scenario }} 52 | runs-on: ubuntu-latest 53 | needs: 'test' 54 | timeout-minutes: 10 55 | 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | try-scenario: 60 | - ember-lts-4.12 61 | - ember-lts-5.4 62 | - ember-release 63 | - ember-beta 64 | - ember-canary 65 | - embroider-safe 66 | - embroider-optimized 67 | 68 | steps: 69 | - uses: actions/checkout@v3 70 | - name: Install Node 71 | uses: actions/setup-node@v3 72 | with: 73 | node-version: 18 74 | cache: npm 75 | - name: Install Dependencies 76 | run: npm ci 77 | - name: Run Tests 78 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }} 79 | -------------------------------------------------------------------------------- /.try.mjs: -------------------------------------------------------------------------------- 1 | // When building your addon for older Ember versions you need to have the required files 2 | const compatFiles = { 3 | 'ember-cli-build.js': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | const { compatBuild } = require('@embroider/compat'); 5 | module.exports = async function (defaults) { 6 | const { buildOnce } = await import('@embroider/vite'); 7 | let app = new EmberApp(defaults); 8 | return compatBuild(app, buildOnce); 9 | };`, 10 | 'config/optional-features.json': JSON.stringify({ 11 | 'application-template-wrapper': false, 12 | 'default-async-observers': true, 13 | 'jquery-integration': false, 14 | 'template-only-glimmer-components': true, 15 | 'no-implicit-route-model': true 16 | }) 17 | }; 18 | 19 | const compatDeps = { 20 | '@embroider/compat': '^4.0.3', 21 | 'ember-cli': '^5.12.0', 22 | 'ember-auto-import': '^2.10.0', 23 | '@ember/optional-features': '^2.2.0' 24 | }; 25 | 26 | export default { 27 | scenarios: [ 28 | { 29 | name: 'ember-lts-5.8', 30 | npm: { 31 | devDependencies: { 32 | 'ember-source': '~5.8.0', 33 | ...compatDeps 34 | } 35 | }, 36 | env: { 37 | ENABLE_COMPAT_BUILD: true 38 | }, 39 | files: compatFiles 40 | }, 41 | { 42 | name: 'ember-lts-5.12', 43 | npm: { 44 | devDependencies: { 45 | 'ember-source': '~5.12.0', 46 | ...compatDeps 47 | } 48 | }, 49 | env: { 50 | ENABLE_COMPAT_BUILD: true 51 | }, 52 | files: compatFiles 53 | }, 54 | { 55 | name: `ember-lts-6.4`, 56 | npm: { 57 | devDependencies: { 58 | 'ember-source': `npm:ember-source@~6.4.0` 59 | } 60 | } 61 | }, 62 | { 63 | name: `ember-latest`, 64 | npm: { 65 | devDependencies: { 66 | 'ember-source': `npm:ember-source@latest` 67 | } 68 | } 69 | }, 70 | { 71 | name: `ember-beta`, 72 | npm: { 73 | devDependencies: { 74 | 'ember-source': `npm:ember-source@beta` 75 | } 76 | } 77 | }, 78 | { 79 | name: `ember-alpha`, 80 | npm: { 81 | devDependencies: { 82 | 'ember-source': `npm:ember-source@alpha` 83 | } 84 | } 85 | } 86 | ] 87 | }; 88 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import { Addon } from '@embroider/addon-dev/rollup'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { resolve, dirname } from 'node:path'; 5 | 6 | const addon = new Addon({ 7 | srcDir: 'src', 8 | destDir: 'dist' 9 | }); 10 | 11 | const rootDirectory = dirname(fileURLToPath(import.meta.url)); 12 | const babelConfig = resolve(rootDirectory, './babel.publish.config.cjs'); 13 | 14 | export default { 15 | // This provides defaults that work well alongside `publicEntrypoints` below. 16 | // You can augment this if you need to. 17 | output: addon.output(), 18 | 19 | plugins: [ 20 | // These are the modules that users should be able to import from your 21 | // addon. Anything not listed here may get optimized away. 22 | // By default all your JavaScript modules (**/*.js) will be importable. 23 | // But you are encouraged to tweak this to only cover the modules that make 24 | // up your addon's public API. Also make sure your package.json#exports 25 | // is aligned to the config here. 26 | // See https://github.com/embroider-build/embroider/blob/main/docs/v2-faq.md#how-can-i-define-the-public-exports-of-my-addon 27 | addon.publicEntrypoints(['**/*.js', 'index.js']), 28 | 29 | // These are the modules that should get reexported into the traditional 30 | // "app" tree. Things in here should also be in publicEntrypoints above, but 31 | // not everything in publicEntrypoints necessarily needs to go here. 32 | addon.appReexports([ 33 | 'components/**/*.js', 34 | 'helpers/**/*.js', 35 | 'modifiers/**/*.js', 36 | 'services/**/*.js' 37 | ]), 38 | 39 | // Follow the V2 Addon rules about dependencies. Your code can import from 40 | // `dependencies` and `peerDependencies` as well as standard Ember-provided 41 | // package names. 42 | addon.dependencies(), 43 | 44 | // This babel config should *not* apply presets or compile away ES modules. 45 | // It exists only to provide development niceties for you, like automatic 46 | // template colocation. 47 | // 48 | // By default, this will load the actual babel config from the file 49 | // babel.config.json. 50 | babel({ 51 | extensions: ['.js', '.gjs'], 52 | babelHelpers: 'bundled', 53 | configFile: babelConfig 54 | }), 55 | 56 | // Ensure that standalone .hbs files are properly integrated as Javascript. 57 | addon.hbs(), 58 | 59 | // Ensure that .gjs files are properly integrated as Javascript 60 | addon.gjs(), 61 | 62 | // addons are allowed to contain imports of .css files, which we want rollup 63 | // to leave alone and keep in the published output. 64 | addon.keepAssets(['**/*.css']), 65 | 66 | // Remove leftover build artifacts when starting a new build. 67 | addon.clean() 68 | ] 69 | }; 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 6.0.1 4 | 5 | - Convert to v2 addon using newer blueprint 6 | 7 | ## 6.0.0 8 | 9 | - Convert to v2 addon 10 | 11 | ## 5.2.2 12 | 13 | - Run ember-cli-update 14 | - Upgrade dependencies 15 | 16 | ## 5.2.1 17 | 18 | - Correct implementation from 5.2.0 19 | 20 | ## 5.2.0 21 | 22 | - Pass options through to `.focus()` 23 | 24 | ## 5.1.3 25 | 26 | - Upgrade dependencies 27 | - Convert tests to `.gjs` 28 | 29 | ## 5.1.2 30 | 31 | - Update `@zestia` scoped packages 32 | 33 | ## 5.1.1 34 | 35 | - Re-release of 5.1.0 but published to GH Packages instead of NPM 36 | 37 | ## 5.1.0 38 | 39 | - Run `ember-cli-update` 40 | 41 | ## 5.0.0 42 | 43 | - Update `ember-modifier` to v4 44 | 45 | ## 4.5.1 46 | 47 | - Fix accidental release of a major version change of `ember-modifier` 48 | 49 | ## 4.5.0 50 | 51 | - Upgrade dependencies 52 | 53 | ## 4.4.0 54 | 55 | - Upgrade dependencies 56 | 57 | ## 4.3.0 58 | 59 | - Resolve ember-modifier deprecations 60 | 61 | ## 4.2.0 62 | 63 | - Upgrade dependencies 64 | - Add Embroider support 65 | - Allow nesting of auto-focus modifier 66 | 67 | ## 4.1.7 68 | 69 | - Upgrade dependencies 70 | 71 | ## 4.1.6 72 | 73 | - Upgrade dependencies 74 | - Run ember-cli-update 75 | 76 | ## 4.1.5 77 | 78 | - Upgrade dependencies 79 | 80 | ## 4.1.4 81 | 82 | - Upgrade dependencies 83 | 84 | ## 4.1.3 85 | 86 | - Upgrade dependencies 87 | 88 | ## 4.1.2 89 | 90 | - Upgrade dependencies 91 | 92 | ## 4.1.1 93 | 94 | - Upgrade dependencies 95 | 96 | ## 4.1.0 97 | 98 | - Correct typo with `programmaticallyFocused` 99 | 100 | ## 4.0.6 101 | 102 | - Upgrade dependencies 103 | 104 | ## 4.0.5 105 | 106 | - Upgrade dependencies 107 | 108 | ## 4.0.4 109 | 110 | - Upgrade dependencies 111 | 112 | ## 4.0.3 113 | 114 | - Upgrade dependencies 115 | 116 | ## 4.0.2 117 | 118 | - Wait until afterRender before focusing 119 | (Fixes focusin not firing on parent node) 120 | 121 | ## 4.0.1 122 | 123 | - Rename export 124 | 125 | ## 4.0.0 126 | 127 | - BREAKING: Switch from a component to an element modifier 128 | 129 | ## 3.0.11 130 | 131 | - Migrate to Glimmer component 132 | 133 | ## 3.0.10 134 | 135 | - Upgrade dependencies 136 | 137 | ## 3.0.9 138 | 139 | - Fix backwards compatibility 140 | 141 | ## 3.0.8 142 | 143 | - Internal refactor 144 | - Upgrade dependencies 145 | 146 | ## 3.0.7 147 | 148 | - Upgrade dependencies 149 | 150 | ## 3.0.6 151 | 152 | - Upgrade dependencies 153 | 154 | ## 3.0.5 155 | 156 | - Upgrade dependencies 157 | 158 | ## 3.0.4 159 | 160 | - Introduce a way to distinguish between user focus and programmatic focus 161 | 162 | ## 3.0.3 163 | 164 | - Update dependencies 165 | 166 | ## 3.0.2 167 | 168 | - Update dependencies 169 | 170 | ## 3.0.0 171 | 172 | - Removes use of positional parameter `{{auto-focus ".my-element"}}` 173 | 174 | ## < 3.0.0 175 | 176 | - No changelog 177 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Debugging: 3 | * https://eslint.org/docs/latest/use/configure/debug 4 | * ---------------------------------------------------- 5 | * 6 | * Print a file's calculated configuration 7 | * 8 | * npx eslint --print-config path/to/file.js 9 | * 10 | * Inspecting the config 11 | * 12 | * npx eslint --inspect-config 13 | * 14 | */ 15 | import babelParser from '@babel/eslint-parser'; 16 | import js from '@eslint/js'; 17 | import prettier from 'eslint-config-prettier'; 18 | import ember from 'eslint-plugin-ember/recommended'; 19 | import importPlugin from 'eslint-plugin-import'; 20 | import n from 'eslint-plugin-n'; 21 | import globals from 'globals'; 22 | import zestia from '@zestia/eslint-config'; 23 | 24 | const esmParserOptions = { 25 | ecmaFeatures: { modules: true }, 26 | ecmaVersion: 'latest' 27 | }; 28 | 29 | const config = [ 30 | js.configs.recommended, 31 | prettier, 32 | ember.configs.base, 33 | ember.configs.gjs, 34 | zestia, 35 | // Temporary 36 | { 37 | rules: { 38 | 'no-restricted-imports': 'off' 39 | } 40 | }, 41 | /** 42 | * Ignores must be in their own object 43 | * https://eslint.org/docs/latest/use/configure/ignore 44 | */ 45 | { 46 | ignores: [ 47 | 'dist/', 48 | 'dist-*/', 49 | 'declarations/', 50 | 'node_modules/', 51 | 'coverage/', 52 | '!**/.*' 53 | ] 54 | }, 55 | /** 56 | * https://eslint.org/docs/latest/use/configure/configuration-files#configuring-linter-options 57 | */ 58 | { 59 | linterOptions: { 60 | reportUnusedDisableDirectives: 'error' 61 | } 62 | }, 63 | { 64 | files: ['**/*.js'], 65 | languageOptions: { 66 | parser: babelParser 67 | } 68 | }, 69 | { 70 | files: ['**/*.{js,gjs}'], 71 | languageOptions: { 72 | parserOptions: esmParserOptions, 73 | globals: { 74 | ...globals.browser 75 | } 76 | } 77 | }, 78 | { 79 | files: ['src/**/*'], 80 | plugins: { 81 | import: importPlugin 82 | }, 83 | rules: { 84 | // require relative imports use full extensions 85 | 'import/extensions': ['error', 'always', { ignorePackages: true }] 86 | } 87 | }, 88 | /** 89 | * CJS node files 90 | */ 91 | { 92 | files: [ 93 | '**/*.cjs', 94 | '.prettierrc.cjs', 95 | '.template-lintrc.cjs', 96 | 'addon-main.cjs' 97 | ], 98 | plugins: { 99 | n 100 | }, 101 | 102 | languageOptions: { 103 | sourceType: 'script', 104 | ecmaVersion: 'latest', 105 | globals: { 106 | ...globals.node 107 | } 108 | } 109 | }, 110 | /** 111 | * ESM node files 112 | */ 113 | { 114 | files: ['**/*.mjs'], 115 | plugins: { 116 | n 117 | }, 118 | 119 | languageOptions: { 120 | sourceType: 'module', 121 | ecmaVersion: 'latest', 122 | parserOptions: esmParserOptions, 123 | globals: { 124 | ...globals.node 125 | } 126 | } 127 | } 128 | ]; 129 | 130 | export default config; 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @zestia/ember-auto-focus 2 | 3 | 4 | 5 | 6 | [npm-badge]: https://img.shields.io/npm/v/@zestia/ember-auto-focus.svg 7 | [npm-badge-url]: https://www.npmjs.com/package/@zestia/ember-auto-focus 8 | [github-actions-badge]: https://github.com/zestia/ember-auto-focus/workflows/CI/badge.svg 9 | [github-actions-url]: https://github.com/zestia/ember-auto-focus/actions 10 | [ember-observer-badge]: https://emberobserver.com/badges/-zestia-ember-auto-focus.svg 11 | [ember-observer-url]: https://emberobserver.com/addons/@zestia/ember-auto-focus 12 | 13 | HTML's `autofocus` attribute focuses an element on the first occurrence of the attribute. But, does nothing on subsequent renders of the same element. 14 | 15 | This addon provides an element modifier, which auto focuses the element when it is inserted into the DOM. 16 | 17 | ## Installation 18 | 19 | ``` 20 | ember install @zestia/ember-auto-focus 21 | ``` 22 | 23 | Add the following to `~/.npmrc` to pull @zestia scoped packages from Github instead of NPM. 24 | 25 | ``` 26 | @zestia:registry=https://npm.pkg.github.com 27 | //npm.pkg.github.com/:_authToken=