├── .changeset ├── README.md ├── config.json └── format-changelogs.cjs ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── ask-for-better-documentation.md │ ├── ask-for-new-feature-or-refactor.md │ ├── give-feedback.md │ ├── report-bug.md │ └── report-outdated-dependency.md └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── docs-app ├── .ember-cli ├── .gitignore ├── .netlifyredirects ├── .prettierignore ├── .stylelintignore ├── .stylelintrc.mjs ├── .template-lintrc.cjs ├── .watchmanconfig ├── CHANGELOG.md ├── app │ ├── app.ts │ ├── assets │ │ ├── app.css │ │ └── app.css.d.ts │ ├── components │ │ ├── .gitkeep │ │ ├── navigation-menu.css │ │ ├── navigation-menu.css.d.ts │ │ ├── navigation-menu.gts │ │ ├── products │ │ │ └── product │ │ │ │ ├── card.css │ │ │ │ ├── card.css.d.ts │ │ │ │ ├── card.gts │ │ │ │ ├── image.css │ │ │ │ ├── image.css.d.ts │ │ │ │ └── image.gts │ │ ├── tracks.gts │ │ ├── tracks │ │ │ ├── list.css │ │ │ ├── list.css.d.ts │ │ │ ├── list.hbs │ │ │ ├── list.ts │ │ │ ├── table.css │ │ │ ├── table.css.d.ts │ │ │ └── table.gts │ │ ├── ui │ │ │ ├── form.css │ │ │ ├── form.css.d.ts │ │ │ ├── form.hbs │ │ │ ├── form.ts │ │ │ ├── form │ │ │ │ ├── checkbox.css │ │ │ │ ├── checkbox.css.d.ts │ │ │ │ ├── checkbox.hbs │ │ │ │ ├── checkbox.ts │ │ │ │ ├── field.css │ │ │ │ ├── field.css.d.ts │ │ │ │ ├── field.gts │ │ │ │ ├── information.css │ │ │ │ ├── information.css.d.ts │ │ │ │ ├── information.gts │ │ │ │ ├── input.css │ │ │ │ ├── input.css.d.ts │ │ │ │ ├── input.hbs │ │ │ │ ├── input.ts │ │ │ │ ├── number.css │ │ │ │ ├── number.css.d.ts │ │ │ │ ├── number.hbs │ │ │ │ ├── number.ts │ │ │ │ ├── textarea.css │ │ │ │ ├── textarea.css.d.ts │ │ │ │ ├── textarea.hbs │ │ │ │ └── textarea.ts │ │ │ ├── page.css │ │ │ ├── page.css.d.ts │ │ │ └── page.gts │ │ └── widgets │ │ │ ├── widget-1.css │ │ │ ├── widget-1.css.d.ts │ │ │ ├── widget-1.gts │ │ │ ├── widget-1 │ │ │ ├── item.css │ │ │ ├── item.css.d.ts │ │ │ └── item.gts │ │ │ ├── widget-2.css │ │ │ ├── widget-2.css.d.ts │ │ │ ├── widget-2.hbs │ │ │ ├── widget-2.ts │ │ │ ├── widget-2 │ │ │ ├── captions.css │ │ │ ├── captions.css.d.ts │ │ │ ├── captions.hbs │ │ │ ├── captions.ts │ │ │ ├── stacked-chart.css │ │ │ ├── stacked-chart.css.d.ts │ │ │ └── stacked-chart.gts │ │ │ ├── widget-3.css │ │ │ ├── widget-3.css.d.ts │ │ │ ├── widget-3.hbs │ │ │ ├── widget-3.ts │ │ │ ├── widget-3 │ │ │ ├── tour-schedule.css │ │ │ ├── tour-schedule.css.d.ts │ │ │ ├── tour-schedule.gts │ │ │ └── tour-schedule │ │ │ │ ├── responsive-image.css │ │ │ │ ├── responsive-image.css.d.ts │ │ │ │ └── responsive-image.gts │ │ │ ├── widget-4.css │ │ │ ├── widget-4.css.d.ts │ │ │ ├── widget-4.gts │ │ │ ├── widget-4 │ │ │ ├── memo.css │ │ │ ├── memo.css.d.ts │ │ │ ├── memo.gts │ │ │ └── memo │ │ │ │ ├── actions.css │ │ │ │ ├── actions.css.d.ts │ │ │ │ ├── actions.gts │ │ │ │ ├── body.css │ │ │ │ ├── body.css.d.ts │ │ │ │ ├── body.gts │ │ │ │ ├── header.css │ │ │ │ ├── header.css.d.ts │ │ │ │ └── header.gts │ │ │ ├── widget-5.css │ │ │ ├── widget-5.css.d.ts │ │ │ └── widget-5.gts │ ├── config │ │ └── environment.d.ts │ ├── controllers │ │ ├── .gitkeep │ │ ├── album.css │ │ ├── album.css.d.ts │ │ ├── album.ts │ │ ├── application.css │ │ ├── application.css.d.ts │ │ ├── application.ts │ │ ├── dashboard.css │ │ ├── dashboard.css.d.ts │ │ ├── dashboard.ts │ │ ├── form.css │ │ ├── form.css.d.ts │ │ ├── form.ts │ │ ├── index.css │ │ ├── index.css.d.ts │ │ ├── index.ts │ │ ├── not-found.css │ │ ├── not-found.css.d.ts │ │ ├── not-found.ts │ │ ├── products.css │ │ ├── products.css.d.ts │ │ └── products.ts │ ├── data │ │ ├── album.ts │ │ ├── concert.ts │ │ ├── index.ts │ │ ├── music-revenue.ts │ │ └── products.ts │ ├── helpers │ │ ├── .gitkeep │ │ └── add.ts │ ├── index.html │ ├── modifiers │ │ ├── draw-stacked-chart.d.ts │ │ ├── draw-stacked-chart.js │ │ └── dynamic-css-grid.ts │ ├── router.ts │ ├── routes │ │ ├── .gitkeep │ │ ├── album.ts │ │ └── products.ts │ ├── styles │ │ ├── app.css │ │ └── app.css.d.ts │ ├── templates │ │ ├── album.hbs │ │ ├── application.hbs │ │ ├── dashboard.hbs │ │ ├── form.hbs │ │ ├── index.hbs │ │ ├── not-found.hbs │ │ └── products.hbs │ └── utils │ │ ├── components │ │ ├── ui │ │ │ └── form │ │ │ │ └── index.ts │ │ └── widgets │ │ │ ├── widget-2.ts │ │ │ └── widget-3.ts │ │ └── routes │ │ └── index.ts ├── config │ ├── dependency-lint.js │ ├── ember-cli-update.json │ ├── environment.js │ ├── optional-features.json │ └── targets.js ├── ember-cli-build.js ├── eslint.config.mjs ├── package.json ├── postcss.config.js ├── prettier.config.mjs ├── public │ ├── assets │ │ └── favicon.png │ ├── images │ │ └── widgets │ │ │ ├── widget-3 │ │ │ ├── venue-extra-wide@1x.jpg │ │ │ ├── venue-extra-wide@2x.jpg │ │ │ ├── venue-extra-wide@4x.jpg │ │ │ ├── venue-square@1x.jpg │ │ │ ├── venue-square@2x.jpg │ │ │ ├── venue-square@4x.jpg │ │ │ ├── venue-wide@1x.jpg │ │ │ ├── venue-wide@2x.jpg │ │ │ └── venue-wide@4x.jpg │ │ │ └── widget-4 │ │ │ └── avatar.jpg │ ├── material-design-icons │ │ ├── alert.svg │ │ ├── alpha-e-box.svg │ │ ├── check.svg │ │ ├── chevron-left.svg │ │ ├── chevron-right.svg │ │ ├── heart-outline.svg │ │ ├── message-processing-outline.svg │ │ ├── share-variant-outline.svg │ │ ├── stop.svg │ │ └── sync.svg │ └── robots.txt ├── testem.js ├── tests │ ├── acceptance │ │ ├── album │ │ │ ├── accessibility-test.ts │ │ │ └── visual-regression-test.ts │ │ ├── dashboard │ │ │ ├── accessibility-test.ts │ │ │ └── visual-regression-test.ts │ │ ├── form │ │ │ ├── accessibility-test.ts │ │ │ └── visual-regression-test.ts │ │ ├── index │ │ │ ├── accessibility-test.ts │ │ │ └── visual-regression-test.ts │ │ ├── not-found │ │ │ └── accessibility-test.ts │ │ └── products │ │ │ ├── accessibility-test.ts │ │ │ └── visual-regression-test.ts │ ├── helpers │ │ ├── index.ts │ │ ├── percy.ts │ │ ├── reset-viewport.ts │ │ └── resize-container.ts │ ├── index.html │ ├── integration │ │ ├── .gitkeep │ │ ├── components │ │ │ ├── navigation-menu-test.gts │ │ │ ├── products │ │ │ │ └── product │ │ │ │ │ ├── card-test.ts │ │ │ │ │ └── image-test.ts │ │ │ ├── tracks-test.ts │ │ │ ├── tracks │ │ │ │ ├── list-test.ts │ │ │ │ └── table-test.ts │ │ │ ├── ui │ │ │ │ ├── form-test.ts │ │ │ │ ├── form │ │ │ │ │ ├── checkbox-test.ts │ │ │ │ │ ├── field-test.ts │ │ │ │ │ ├── information-test.ts │ │ │ │ │ ├── input-test.ts │ │ │ │ │ ├── number-test.ts │ │ │ │ │ └── textarea-test.ts │ │ │ │ └── page-test.ts │ │ │ └── widgets │ │ │ │ ├── widget-1-test.ts │ │ │ │ ├── widget-2-test.ts │ │ │ │ ├── widget-3-test.ts │ │ │ │ ├── widget-4-test.ts │ │ │ │ └── widget-5-test.ts │ │ └── modifiers │ │ │ ├── draw-stacked-chart-test.ts │ │ │ └── dynamic-css-grid-test.ts │ ├── test-helper.ts │ └── unit │ │ ├── .gitkeep │ │ └── utils │ │ └── components │ │ ├── ui │ │ └── form │ │ │ └── index-test.ts │ │ └── widgets │ │ ├── widget-2-test.ts │ │ └── widget-3-test.ts ├── tsconfig.json └── types │ └── global.d.ts ├── package.json ├── packages └── ember-container-query │ ├── .gitignore │ ├── .prettierignore │ ├── .template-lintrc.cjs │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── addon-main.cjs │ ├── babel.config.json │ ├── eslint.config.mjs │ ├── package.json │ ├── prettier.config.mjs │ ├── rollup.config.mjs │ ├── src │ ├── components │ │ ├── container-query.css │ │ └── container-query.gts │ ├── helpers │ │ ├── aspect-ratio.ts │ │ ├── height.ts │ │ └── width.ts │ ├── index.ts │ ├── modifiers │ │ └── container-query.ts │ └── template-registry.ts │ ├── tsconfig.json │ └── unpublished-development-types │ └── index.d.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── test-app ├── .ember-cli ├── .gitignore ├── .prettierignore ├── .template-lintrc.cjs ├── .watchmanconfig ├── CHANGELOG.md ├── app ├── app.ts ├── components │ └── .gitkeep ├── config │ └── environment.d.ts ├── controllers │ └── .gitkeep ├── helpers │ └── .gitkeep ├── index.html ├── router.ts ├── routes │ └── .gitkeep ├── styles │ └── app.css └── templates │ ├── application.hbs │ └── index.hbs ├── config ├── dependency-lint.js ├── ember-cli-update.json ├── ember-try.js ├── environment.js ├── optional-features.json └── targets.js ├── ember-cli-build.js ├── eslint.config.mjs ├── package.json ├── prettier.config.mjs ├── public ├── assets │ └── favicon.png └── robots.txt ├── testem.js ├── tests ├── acceptance │ └── index │ │ └── accessibility-test.ts ├── helpers │ ├── container-query.ts │ ├── container-query │ │ ├── assert-data-attributes.ts │ │ ├── assert-dimensions.ts │ │ └── assert-features.ts │ ├── index.ts │ └── resize-container.ts ├── index.html ├── integration │ ├── .gitkeep │ ├── components │ │ └── container-query │ │ │ ├── dataAttributePrefix-test.ts │ │ │ ├── debounce-test.ts │ │ │ ├── features-test.ts │ │ │ ├── splattributes-test.ts │ │ │ └── tagName-test.ts │ ├── helpers │ │ ├── aspect-ratio-test.ts │ │ ├── height-test.ts │ │ └── width-test.ts │ └── modifiers │ │ └── container-query-test.ts └── test-helper.ts ├── tsconfig.json └── types └── global.d.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", 3 | "changelog": "./format-changelogs.cjs", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.changeset/format-changelogs.cjs: -------------------------------------------------------------------------------- 1 | const { getInfo } = require('@changesets/get-github-info'); 2 | 3 | const repo = 'ijlee2/ember-container-query'; 4 | 5 | async function analyze(changeset) { 6 | const { links: info } = await getInfo({ 7 | commit: changeset.commit, 8 | repo, 9 | }); 10 | 11 | const contributor = info.user ? `(${info.user})` : undefined; 12 | const link = info.pull ?? info.commit ?? undefined; 13 | const summary = (changeset.summary ?? '').split('\n')[0].trim(); 14 | 15 | return { 16 | contributor, 17 | link, 18 | summary, 19 | }; 20 | } 21 | 22 | async function summarize(changeset) { 23 | const { contributor, link, summary } = await analyze(changeset); 24 | 25 | const line = [link, summary, contributor].filter(Boolean).join(' '); 26 | 27 | return `- ${line}`; 28 | } 29 | 30 | async function getDependencyReleaseLine(changesets) { 31 | try { 32 | const lines = await Promise.all(changesets.map(summarize)); 33 | 34 | return lines.join('\n'); 35 | } catch (error) { 36 | console.error(`ERROR: getDependencyReleaseLine (${error.message})`); 37 | 38 | return ''; 39 | } 40 | } 41 | 42 | async function getReleaseLine(changeset) { 43 | try { 44 | return summarize(changeset); 45 | } catch (error) { 46 | console.error(`ERROR: getReleaseLine (${error.message})`); 47 | 48 | return ''; 49 | } 50 | } 51 | 52 | module.exports = { 53 | getDependencyReleaseLine, 54 | getReleaseLine, 55 | }; 56 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ask-for-better-documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask for better documentation 3 | about: Ask for better documentation 4 | title: '' 5 | labels: 'enhance: documentation' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hello! Thanks for taking time to suggest how we can improve documentation. 11 | 12 | Here, documentation can mean a few different things, including README, code comments, and tests. Anything that will help everyone understand how to use `ember-container-query`! 13 | 14 | 15 | ## I would like to see... 🙋‍♀️🙋‍♂️ 16 | 17 | A clear, concise description of what you want to happen. 18 | 19 | 20 | ## Why and how 💬 21 | 22 | A clear, concise description of why you want something to happen and how we might be able to solve the problem. 23 | 24 | 25 | ## Additional context ➕ 26 | 27 | If needed, you can provide more context (e.g. reference materials, screenshots, GIFs) for the request here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ask-for-new-feature-or-refactor.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask for new feature or refactor 3 | about: Ask for new feature or refactor 4 | title: '' 5 | labels: 'enhance: code' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hello! Thanks for taking time to suggest how we can improve `ember-container-query`. 11 | 12 | Before you make a new issue, please search for similar issues. It's possible that someone has made a request already. 13 | 14 | 15 | ## I would like to see... 🙋‍♀️🙋‍♂️ 16 | 17 | A clear, concise description of what you want to happen. 18 | 19 | 20 | ## Why and how 💬 21 | 22 | A clear, concise description of why you want something to happen and how we might be able to solve the problem. 23 | 24 | 25 | ## Additional context ➕ 26 | 27 | If needed, you can provide more context (e.g. reference materials, screenshots, GIFs) for the request here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/give-feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Give feedback 3 | about: Give feedback 4 | title: '' 5 | labels: user feedback 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hello! Thanks for taking time to give feedback. 11 | 12 | You can tell us: 13 | 14 | - How you used `ember-container-query` 15 | - What you liked about it 16 | - What you didn't like about it 17 | 18 | When sharing what you didn't like, please do give constructive feedback by suggesting a specific solution for how `ember-container-query` can be improved. 19 | 20 | 21 | ## Share your story 💞 22 | 23 | (My team and) I ... -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report bug 3 | about: Report bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hello! Thanks for taking time to make a bug report. 11 | 12 | Before you make a new issue, please search for similar issues. It's possible that someone has reported this bug already. 13 | 14 | 15 | ## Describe the bug 🐞 16 | 17 | A clear and concise description of what the bug is. 18 | 19 | 20 | ## Expected behavior 🤔 21 | 22 | A clear and concise description of what you expected to happen. 23 | 24 | 25 | ## Minimal reproduction 🔬 26 | 27 | Describe steps to reproduce the behavior: 28 | 29 | 1. Go to '...' 30 | 2. Click on '...' 31 | 3. Scroll down to '...' 32 | 4. See error 33 | 34 | If possible, please, share a link with a minimal reproduction. 35 | 36 | 41 | 42 | 43 | ## Environment 🌍 44 | 45 | - Ember: - 46 | - Node.js/npm: - 47 | - OS: - 48 | - Browser: - 49 | 50 | 51 | ## Additional context ➕ 52 | 53 | If needed, you can provide more context (e.g. reference materials, screenshots, GIFs) for the problem here. 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report-outdated-dependency.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report outdated dependency 3 | about: Report outdated dependency 4 | title: '' 5 | labels: 'enhance: dependency' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hello! Thanks for taking time to make an outdated dependency report. 11 | 12 | Before you make a new issue, please search for similar issues. It's possible that someone has made a request for update already. 13 | 14 | 15 | ## List outdated dependencies 🔗 16 | 17 | When you ran `pnpm outdated -r`, what did you see? 18 | 19 | ```sh 20 | ┌───────────────┬─────────┬────────┬────────────────────────────────┐ 21 | │ Package │ Current │ Latest │ Dependents │ 22 | ├───────────────┼─────────┼────────┼────────────────────────────────┤ 23 | │ rollup (dev) │ 3.21.3 │ 3.22.0 │ ember-container-query │ 24 | ├───────────────┼─────────┼────────┼────────────────────────────────┤ 25 | │ webpack (dev) │ 5.81.0 │ 5.82.1 │ docs-app, test-app │ 26 | └───────────────┴─────────┴────────┴────────────────────────────────┘ 27 | ``` 28 | 29 | 30 | ## Risk analysis ⚠️ 31 | 32 | Are there breaking changes that we should be aware of? Please add links to the `CHANGELOG`s, if they are available. 33 | 34 | 35 | ## Additional context ➕ 36 | 37 | If needed, you can provide more context (e.g. reference materials, screenshots, GIFs) for the problem here. 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | dist/ 3 | tmp/ 4 | 5 | # dependencies 6 | node_modules/ 7 | 8 | # misc 9 | .DS_Store 10 | .env* 11 | .eslintcache 12 | /.pnpm-debug.log 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # https://github.com/emberjs/rfcs/pull/907 2 | auto-install-peers=false 3 | resolve-peers-from-workspace-root=false 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ember-container-query 2 | 3 | You can visit [`packages/ember-container-query/CHANGELOG.md`](./packages/ember-container-query/CHANGELOG.md) for more information. 4 | 5 | ## Version overview 6 | 7 | - [5.0.0](./packages/ember-container-query/CHANGELOG.md#500-2023-09-14) 8 | - [4.0.0](./packages/ember-container-query/CHANGELOG.md#400-2023-05-01) - [4.1.0](./packages/ember-container-query/CHANGELOG.md#410-2023-09-14) 9 | - [3.0.0](./packages/ember-container-query/CHANGELOG.md#300-2022-12-15) - [3.2.0](./packages/ember-container-query/CHANGELOG.md#320-2023-01-05) 10 | - [2.0.0](./packages/ember-container-query/CHANGELOG.md#200-2022-04-09) - [2.1.1](./packages/ember-container-query/CHANGELOG.md#211-2022-12-02) 11 | - [1.0.0](./packages/ember-container-query/CHANGELOG.md#100-2020-05-29) - [1.1.9](./packages/ember-container-query/CHANGELOG.md#119-2021-08-25) 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Isaac J. Lee 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 | -------------------------------------------------------------------------------- /docs-app/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript 4 | rather than JavaScript by default, when a TypeScript version of a given blueprint is available. 5 | */ 6 | "isTypeScriptProject": true 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /declarations/ 3 | /dist/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.env* 10 | /.pnp* 11 | /.eslintcache 12 | /.pnpm-debug.log 13 | /.stylelintcache 14 | /coverage/ 15 | /testem.log 16 | 17 | # broccoli-debug 18 | /DEBUG/ 19 | -------------------------------------------------------------------------------- /docs-app/.netlifyredirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /docs-app/.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # misc 8 | /coverage/ 9 | !.* 10 | .*/ 11 | *.html 12 | 13 | # specific to this package 14 | -------------------------------------------------------------------------------- /docs-app/.stylelintignore: -------------------------------------------------------------------------------- 1 | # unconventional files 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | -------------------------------------------------------------------------------- /docs-app/.stylelintrc.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@ijlee2-frontend-configs/stylelint/css-modules'; 2 | -------------------------------------------------------------------------------- /docs-app/.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('@ijlee2-frontend-configs/ember-template-lint'); 4 | -------------------------------------------------------------------------------- /docs-app/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["dist"] 3 | } 4 | -------------------------------------------------------------------------------- /docs-app/app/app.ts: -------------------------------------------------------------------------------- 1 | import './assets/app.css'; 2 | 3 | import Application from '@ember/application'; 4 | import loadInitializers from 'ember-load-initializers'; 5 | import Resolver from 'ember-resolver'; 6 | 7 | import config from './config/environment'; 8 | 9 | export default class App extends Application { 10 | modulePrefix = config.modulePrefix; 11 | podModulePrefix = config.podModulePrefix; 12 | Resolver = Resolver; 13 | } 14 | 15 | loadInitializers(App, config.modulePrefix); 16 | -------------------------------------------------------------------------------- /docs-app/app/assets/app.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: Record; 2 | 3 | export default styles; 4 | -------------------------------------------------------------------------------- /docs-app/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/app/components/.gitkeep -------------------------------------------------------------------------------- /docs-app/app/components/navigation-menu.css: -------------------------------------------------------------------------------- 1 | .list { 2 | align-items: center; 3 | display: flex; 4 | } 5 | 6 | .link { 7 | display: inline-block; 8 | font-size: 0.875rem; 9 | padding: 0.875rem 1rem; 10 | text-decoration: none; 11 | white-space: nowrap; 12 | } 13 | 14 | .link:global(.active) { 15 | background-color: #15202d; 16 | } 17 | 18 | .link:hover { 19 | background-color: #26313d; 20 | transition: background-color 0.17s; 21 | } 22 | -------------------------------------------------------------------------------- /docs-app/app/components/navigation-menu.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'link': string; 3 | readonly 'list': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/navigation-menu.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { LinkTo } from '@ember/routing'; 3 | import { local } from 'embroider-css-modules'; 4 | 5 | import styles from './navigation-menu.css'; 6 | 7 | type MenuItem = { 8 | label: string; 9 | route: string; 10 | }; 11 | 12 | interface NavigationMenuSignature { 13 | Args: { 14 | menuItems: MenuItem[]; 15 | name?: string; 16 | }; 17 | } 18 | 19 | const NavigationMenuComponent: TOC = ; 36 | 37 | export default NavigationMenuComponent; 38 | 39 | declare module '@glint/environment-ember-loose/registry' { 40 | export default interface Registry { 41 | NavigationMenu: typeof NavigationMenuComponent; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs-app/app/components/products/product/card.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'actions': string; 3 | readonly 'body': string; 4 | readonly 'container': string; 5 | readonly 'description': string; 6 | readonly 'header': string; 7 | readonly 'image-container': string; 8 | readonly 'link': string; 9 | readonly 'name': string; 10 | readonly 'price': string; 11 | }; 12 | 13 | export default styles; 14 | -------------------------------------------------------------------------------- /docs-app/app/components/products/product/card.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { hash } from '@ember/helper'; 3 | import { LinkTo } from '@ember/routing'; 4 | import { ContainerQuery, width } from 'ember-container-query'; 5 | 6 | import type { Product } from '../../../data'; 7 | import styles from './card.css'; 8 | import ProductsProductImage from './image'; 9 | 10 | function formatPrice(price: number): string { 11 | return `$${price}`; 12 | } 13 | 14 | interface ProductsProductCardSignature { 15 | Args: { 16 | product: Product; 17 | redirectTo?: string; 18 | }; 19 | } 20 | 21 | const ProductsProductCardComponent: TOC = 22 | ; 61 | 62 | export default ProductsProductCardComponent; 63 | 64 | declare module '@glint/environment-ember-loose/registry' { 65 | export default interface Registry { 66 | 'Products::Product::Card': typeof ProductsProductCardComponent; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs-app/app/components/products/product/image.css: -------------------------------------------------------------------------------- 1 | .image, 2 | .placeholder-image { 3 | aspect-ratio: 4 / 3; 4 | border-radius: 0.75rem; 5 | width: 100%; 6 | } 7 | 8 | .image { 9 | object-fit: cover; 10 | } 11 | 12 | .placeholder-image { 13 | background: linear-gradient( 14 | 36deg, 15 | rgb(255 224 130 / 40%) 15%, 16 | rgb(255 248 225 / 80%) 90% 17 | ); 18 | min-width: 8rem; 19 | } 20 | -------------------------------------------------------------------------------- /docs-app/app/components/products/product/image.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'image': string; 3 | readonly 'placeholder-image': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/products/product/image.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import config from 'docs-app/config/environment'; 3 | 4 | import styles from './image.css'; 5 | 6 | const isTestEnvironment = config.environment === 'test'; 7 | 8 | interface ProductsProductImageSignature { 9 | Args: { 10 | src: string; 11 | }; 12 | } 13 | 14 | const ProductsProductImageComponent: TOC = 15 | ; 26 | 27 | export default ProductsProductImageComponent; 28 | 29 | declare module '@glint/environment-ember-loose/registry' { 30 | export default interface Registry { 31 | 'Products::Product::Image': typeof ProductsProductImageComponent; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { hash } from '@ember/helper'; 3 | import { ContainerQuery, height, width } from 'ember-container-query'; 4 | import { and } from 'ember-truth-helpers'; 5 | 6 | import type { Track } from '../data'; 7 | import TracksList from './tracks/list'; 8 | import TracksTable from './tracks/table'; 9 | 10 | interface TracksSignature { 11 | Args: { 12 | tracks?: Track[]; 13 | }; 14 | } 15 | 16 | const TracksComponent: TOC = ; 38 | 39 | export default TracksComponent; 40 | 41 | declare module '@glint/environment-ember-loose/registry' { 42 | export default interface Registry { 43 | Tracks: typeof TracksComponent; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks/list.css: -------------------------------------------------------------------------------- 1 | .list { 2 | display: grid; 3 | gap: 0.25rem 0.5rem; 4 | grid-auto-flow: column; 5 | } 6 | 7 | .item { 8 | align-items: center; 9 | display: flex; 10 | justify-content: space-between; 11 | min-height: 1.5rem; 12 | word-break: break-all; 13 | } 14 | 15 | .icon-explicit { 16 | color: rgb(247 252 251 / 90%); 17 | flex-shrink: 0; 18 | margin-left: 0.25rem; 19 | } 20 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks/list.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'icon-explicit': string; 3 | readonly 'item': string; 4 | readonly 'list': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks/list.hbs: -------------------------------------------------------------------------------- 1 |
    10 | {{#each @tracks as |track index|}} 11 |
  • 12 |
    13 | {{add index 1}}. 14 | 15 | {{track.name}} 16 | 17 |
    18 | 19 | {{#if track.isExplicit}} 20 | 21 | {{svg-jar 22 | "alpha-e-box" 23 | class=this.styles.icon-explicit 24 | desc="Letter E, which stands for explicit" 25 | role="img" 26 | }} 27 | 28 | {{/if}} 29 |
  • 30 | {{/each}} 31 |
-------------------------------------------------------------------------------- /docs-app/app/components/tracks/list.ts: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | 3 | import type { Track } from '../../data'; 4 | import styles from './list.css'; 5 | 6 | interface TracksListSignature { 7 | Args: { 8 | numColumns?: number; 9 | tracks?: Track[]; 10 | }; 11 | } 12 | 13 | export default class TracksListComponent extends Component { 14 | styles = styles; 15 | 16 | get numColumns(): number { 17 | const { numColumns } = this.args; 18 | 19 | return numColumns ?? 1; 20 | } 21 | 22 | get numRows(): number { 23 | const { tracks } = this.args; 24 | 25 | if (!tracks) { 26 | return 0; 27 | } 28 | 29 | return Math.ceil(tracks.length / this.numColumns); 30 | } 31 | } 32 | 33 | declare module '@glint/environment-ember-loose/registry' { 34 | export default interface Registry { 35 | 'Tracks::List': typeof TracksListComponent; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks/table.css: -------------------------------------------------------------------------------- 1 | .track-number { 2 | width: 2rem; 3 | } 4 | 5 | .track-is-explicit { 6 | width: 4rem; 7 | } 8 | 9 | .track-length { 10 | width: 4rem; 11 | } 12 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks/table.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'track-is-explicit': string; 3 | readonly 'track-length': string; 4 | readonly 'track-number': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/tracks/table.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | 3 | import type { Track } from '../../data'; 4 | import add from '../../helpers/add'; 5 | import styles from './table.css'; 6 | 7 | interface TracksTableSignature { 8 | Args: { 9 | tracks?: Track[]; 10 | }; 11 | } 12 | 13 | const TracksTableComponent: TOC = ; 44 | 45 | export default TracksTableComponent; 46 | 47 | declare module '@glint/environment-ember-loose/registry' { 48 | export default interface Registry { 49 | 'Tracks::Table': typeof TracksTableComponent; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form.css: -------------------------------------------------------------------------------- 1 | .form { 2 | background-color: #15202d; 3 | border-radius: 0.25rem; 4 | box-shadow: inset 0 0 0.125rem #26313d; 5 | display: flex; 6 | flex-direction: column; 7 | padding: 1.25rem 1.5rem; 8 | } 9 | 10 | .actions { 11 | align-items: center; 12 | display: flex; 13 | justify-content: flex-end; 14 | margin-top: 2rem; 15 | } 16 | 17 | .submit-button { 18 | padding: 0.5rem 3rem; 19 | } 20 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'actions': string; 3 | readonly 'form': string; 4 | readonly 'submit-button': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form.hbs: -------------------------------------------------------------------------------- 1 |
11 | 16 | 17 | 21 | {{yield 22 | (hash 23 | Checkbox=(component 24 | "ui/form/checkbox" 25 | changeset=this.changeset 26 | isInline=true 27 | isWide=CQ.features.wide 28 | onUpdate=this.updateChangeset 29 | ) 30 | Input=(component 31 | "ui/form/input" 32 | changeset=this.changeset 33 | isWide=CQ.features.wide 34 | onUpdate=this.updateChangeset 35 | ) 36 | Number=(component 37 | "ui/form/number" 38 | changeset=this.changeset 39 | isWide=CQ.features.wide 40 | onUpdate=this.updateChangeset 41 | ) 42 | Textarea=(component 43 | "ui/form/textarea" 44 | changeset=this.changeset 45 | isWide=CQ.features.wide 46 | onUpdate=this.updateChangeset 47 | ) 48 | ) 49 | }} 50 | 51 | 52 |
53 | 60 |
61 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/checkbox.css: -------------------------------------------------------------------------------- 1 | .checkbox { 2 | align-items: center; 3 | background-color: white; 4 | border: 0.125rem solid #ffd54f; 5 | cursor: pointer; 6 | display: flex; 7 | height: 1rem; 8 | justify-content: center; 9 | position: relative; 10 | width: 1rem; 11 | } 12 | 13 | .checkbox:focus { 14 | background-color: #ffecb3; 15 | outline: 0; 16 | } 17 | 18 | .checkbox:not(:focus) { 19 | border-color: transparent; 20 | } 21 | 22 | .checkmark-icon { 23 | color: white; 24 | } 25 | 26 | .checkbox.is-checked { 27 | background-color: #1976d2; 28 | } 29 | 30 | .is-disabled { 31 | composes: input-disabled from global; 32 | } 33 | 34 | .is-disabled .checkmark-icon { 35 | color: #546e7a; 36 | } 37 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/checkbox.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'checkbox': string; 3 | readonly 'checkmark-icon': string; 4 | readonly 'is-checked': string; 5 | readonly 'is-disabled': string; 6 | }; 7 | 8 | export default styles; 9 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/checkbox.hbs: -------------------------------------------------------------------------------- 1 | 6 | <:label as |l|> 7 | 16 | 17 | 18 | <:field as |f|> 19 | 37 | {{#if this.isChecked}} 38 | {{svg-jar 39 | "check" 40 | class=this.styles.checkmark-icon 41 | desc="A checkmark to indicate that the input field is checked" 42 | role="img" 43 | }} 44 | {{/if}} 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/checkbox.ts: -------------------------------------------------------------------------------- 1 | import { action, get } from '@ember/object'; 2 | import Component from '@glimmer/component'; 3 | 4 | import { generateErrorMessage } from '../../../utils/components/ui/form'; 5 | import styles from './checkbox.css'; 6 | 7 | interface UiFormCheckboxSignature { 8 | Args: { 9 | changeset: Record; 10 | isDisabled?: boolean; 11 | isInline?: boolean; 12 | isReadOnly?: boolean; 13 | isRequired?: boolean; 14 | isWide?: boolean; 15 | key: string; 16 | label: string; 17 | onUpdate: ({ key, value }: { key: string; value: any }) => void; 18 | }; 19 | } 20 | 21 | export default class UiFormCheckboxComponent extends Component { 22 | styles = styles; 23 | 24 | get errorMessage(): string | undefined { 25 | const { isRequired } = this.args; 26 | 27 | return generateErrorMessage({ 28 | isRequired, 29 | value: this.isChecked, 30 | valueType: 'boolean', 31 | }); 32 | } 33 | 34 | get isChecked(): boolean { 35 | const { changeset, key } = this.args; 36 | 37 | return (get(changeset, key) as boolean) ?? false; 38 | } 39 | 40 | @action updateValue(): void { 41 | const { isDisabled, isReadOnly, key, onUpdate } = this.args; 42 | 43 | if (isDisabled || isReadOnly) { 44 | return; 45 | } 46 | 47 | const value = !this.isChecked; 48 | 49 | onUpdate({ key, value }); 50 | } 51 | 52 | @action updateValueByPressingSpace(event: KeyboardEvent): void { 53 | if (event.code === 'Space' || event.key === 'Space') { 54 | this.updateValue(); 55 | } 56 | } 57 | } 58 | 59 | declare module '@glint/environment-ember-loose/registry' { 60 | export default interface Registry { 61 | 'Ui::Form::Checkbox': typeof UiFormCheckboxComponent; 62 | 'ui/form/checkbox': typeof UiFormCheckboxComponent; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/field.css: -------------------------------------------------------------------------------- 1 | .container { 2 | align-items: start; 3 | display: grid; 4 | } 5 | 6 | .container:not(.is-wide, .no-feedback) { 7 | grid-column-gap: 0; 8 | grid-row-gap: 0.5rem; 9 | grid-template-areas: 10 | "label" 11 | "field" 12 | "feedback"; 13 | grid-template-columns: 1fr; 14 | grid-template-rows: auto 1fr auto; 15 | } 16 | 17 | .container:not(.is-wide).no-feedback { 18 | grid-column-gap: 0; 19 | grid-row-gap: 0.5rem; 20 | grid-template-areas: 21 | "label" 22 | "field"; 23 | grid-template-columns: 1fr; 24 | grid-template-rows: auto 1fr; 25 | } 26 | 27 | .container.is-wide:not(.no-feedback) { 28 | grid-column-gap: 1rem; 29 | grid-row-gap: 0.5rem; 30 | grid-template-areas: 31 | "label field" 32 | "label feedback"; 33 | grid-template-columns: 10rem 1fr; 34 | grid-template-rows: 1fr auto; 35 | } 36 | 37 | .container.is-wide.no-feedback { 38 | grid-column-gap: 1rem; 39 | grid-row-gap: 0.5rem; 40 | grid-template-areas: "label field"; 41 | grid-template-columns: 10rem 1fr; 42 | grid-template-rows: 1fr; 43 | } 44 | 45 | .label { 46 | grid-area: label; 47 | overflow: hidden; 48 | word-break: break-all; 49 | } 50 | 51 | .field { 52 | grid-area: field; 53 | } 54 | 55 | .feedback { 56 | align-items: center; 57 | display: flex; 58 | font-size: 0.875rem; 59 | grid-area: feedback; 60 | } 61 | 62 | .feedback.is-error { 63 | color: #ff5252; 64 | } 65 | 66 | .message { 67 | margin-left: 0.5rem; 68 | } 69 | 70 | /* Exceptions for mobile */ 71 | .container.is-inline:not(.is-wide, .no-feedback) { 72 | grid-column-gap: 1rem; 73 | grid-row-gap: 0.5rem; 74 | grid-template-areas: 75 | "field label" 76 | "feedback feedback"; 77 | grid-template-columns: auto 1fr; 78 | grid-template-rows: 1fr auto; 79 | } 80 | 81 | .container.is-inline:not(.is-wide).no-feedback { 82 | grid-column-gap: 1rem; 83 | grid-row-gap: 0; 84 | grid-template-areas: "field label"; 85 | grid-template-columns: auto 1fr; 86 | grid-template-rows: 1fr; 87 | } 88 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/field.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'container': string; 3 | readonly 'feedback': string; 4 | readonly 'field': string; 5 | readonly 'is-error': string; 6 | readonly 'is-inline': string; 7 | readonly 'is-wide': string; 8 | readonly 'label': string; 9 | readonly 'message': string; 10 | readonly 'no-feedback': string; 11 | }; 12 | 13 | export default styles; 14 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/information.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-bottom: 1rem; 3 | } 4 | 5 | .title { 6 | font-size: 1.5rem; 7 | font-weight: 700; 8 | margin-bottom: 0.5rem; 9 | } 10 | 11 | .instructions { 12 | font-size: 0.9rem; 13 | font-weight: 300; 14 | line-height: 1.25rem; 15 | margin-bottom: 0.5rem; 16 | } 17 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/information.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'container': string; 3 | readonly 'instructions': string; 4 | readonly 'title': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/information.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { concat } from '@ember/helper'; 3 | import { or } from 'ember-truth-helpers'; 4 | 5 | import styles from './information.css'; 6 | 7 | interface UiFormInformationSignature { 8 | Args: { 9 | formId: string; 10 | instructions?: string; 11 | title?: string; 12 | }; 13 | } 14 | 15 | const UiFormInformationComponent: TOC = ; 40 | 41 | export default UiFormInformationComponent; 42 | 43 | declare module '@glint/environment-ember-loose/registry' { 44 | export default interface Registry { 45 | 'Ui::Form::Information': typeof UiFormInformationComponent; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/input.css: -------------------------------------------------------------------------------- 1 | .input { 2 | border: 0.125rem solid #ffd54f; 3 | padding: 0.125rem 0.25rem; 4 | width: calc(100% - 0.75rem); 5 | } 6 | 7 | .input:focus { 8 | background-color: #ffecb3; 9 | outline: 0; 10 | } 11 | 12 | .input:not(:focus) { 13 | border-color: transparent; 14 | } 15 | 16 | .input::placeholder { 17 | font-style: italic; 18 | } 19 | 20 | .is-disabled { 21 | composes: input-disabled from global; 22 | } 23 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/input.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'input': string; 3 | readonly 'is-disabled': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/input.hbs: -------------------------------------------------------------------------------- 1 | 5 | <:label as |l|> 6 | 15 | 16 | 17 | <:field as |f|> 18 | 34 | 35 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/input.ts: -------------------------------------------------------------------------------- 1 | import { assert } from '@ember/debug'; 2 | import { action, get } from '@ember/object'; 3 | import Component from '@glimmer/component'; 4 | 5 | import { generateErrorMessage } from '../../../utils/components/ui/form'; 6 | import styles from './input.css'; 7 | 8 | interface UiFormInputSignature { 9 | Args: { 10 | changeset: Record; 11 | isDisabled?: boolean; 12 | isReadOnly?: boolean; 13 | isRequired?: boolean; 14 | isWide?: boolean; 15 | key: string; 16 | label: string; 17 | onUpdate: ({ key, value }: { key: string; value: any }) => void; 18 | placeholder?: string; 19 | type?: string; 20 | }; 21 | } 22 | 23 | export default class UiFormInputComponent extends Component { 24 | styles = styles; 25 | 26 | get errorMessage(): string | undefined { 27 | const { isRequired } = this.args; 28 | 29 | return generateErrorMessage({ 30 | isRequired, 31 | value: this.value, 32 | valueType: 'string', 33 | }); 34 | } 35 | 36 | get type(): string { 37 | const { type } = this.args; 38 | 39 | assert( 40 | 'To render a number input, please use instead.', 41 | type !== 'number', 42 | ); 43 | 44 | return this.args.type ?? 'text'; 45 | } 46 | 47 | get value(): string { 48 | const { changeset, key } = this.args; 49 | 50 | return ((get(changeset, key) as string) ?? '').toString(); 51 | } 52 | 53 | @action updateValue(event: Event): void { 54 | const { key, onUpdate } = this.args; 55 | const { value } = event.target as HTMLInputElement; 56 | 57 | onUpdate({ key, value }); 58 | } 59 | } 60 | 61 | declare module '@glint/environment-ember-loose/registry' { 62 | export default interface Registry { 63 | 'Ui::Form::Input': typeof UiFormInputComponent; 64 | 'ui/form/input': typeof UiFormInputComponent; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/number.css: -------------------------------------------------------------------------------- 1 | .input { 2 | composes: input from "./input.css"; 3 | } 4 | 5 | .is-disabled { 6 | composes: input-disabled from global; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/number.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'input': string; 3 | readonly 'is-disabled': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/number.hbs: -------------------------------------------------------------------------------- 1 | 5 | <:label as |l|> 6 | 15 | 16 | 17 | <:field as |f|> 18 | 37 | 38 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/number.ts: -------------------------------------------------------------------------------- 1 | import { action, get } from '@ember/object'; 2 | import Component from '@glimmer/component'; 3 | 4 | import { generateErrorMessage } from '../../../utils/components/ui/form'; 5 | import styles from './number.css'; 6 | 7 | interface UiFormNumberSignature { 8 | Args: { 9 | changeset: Record; 10 | isDisabled?: boolean; 11 | isReadOnly?: boolean; 12 | isRequired?: boolean; 13 | isWide?: boolean; 14 | key: string; 15 | label: string; 16 | maxValue?: number; 17 | minValue?: number; 18 | onUpdate: ({ key, value }: { key: string; value: any }) => void; 19 | placeholder?: string; 20 | step?: number | 'any'; 21 | type?: string; 22 | }; 23 | } 24 | 25 | export default class UiFormNumberComponent extends Component { 26 | styles = styles; 27 | 28 | get errorMessage(): string | undefined { 29 | const { isRequired } = this.args; 30 | 31 | return generateErrorMessage({ 32 | isRequired, 33 | value: this.value, 34 | valueType: 'number', 35 | }); 36 | } 37 | 38 | get value(): string { 39 | const { changeset, key } = this.args; 40 | 41 | return ((get(changeset, key) as string) ?? '').toString(); 42 | } 43 | 44 | @action updateValue(event: Event): void { 45 | const { key, onUpdate } = this.args; 46 | const { value } = event.target as HTMLInputElement; 47 | 48 | const valueAsNumber = Number.parseFloat(value); 49 | 50 | if (Number.isNaN(valueAsNumber)) { 51 | onUpdate({ key, value: undefined }); 52 | return; 53 | } 54 | 55 | onUpdate({ key, value: valueAsNumber }); 56 | } 57 | } 58 | 59 | declare module '@glint/environment-ember-loose/registry' { 60 | export default interface Registry { 61 | 'Ui::Form::Number': typeof UiFormNumberComponent; 62 | 'ui/form/number': typeof UiFormNumberComponent; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/textarea.css: -------------------------------------------------------------------------------- 1 | .textarea { 2 | composes: input from "./input.css"; 3 | } 4 | 5 | .is-disabled { 6 | composes: input-disabled from global; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/textarea.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'is-disabled': string; 3 | readonly 'textarea': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/textarea.hbs: -------------------------------------------------------------------------------- 1 | 5 | <:label as |l|> 6 | 15 | 16 | 17 | <:field as |f|> 18 | 34 | 35 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/form/textarea.ts: -------------------------------------------------------------------------------- 1 | import { action, get } from '@ember/object'; 2 | import Component from '@glimmer/component'; 3 | 4 | import { generateErrorMessage } from '../../../utils/components/ui/form'; 5 | import styles from './textarea.css'; 6 | 7 | interface UiFormTextareaSignature { 8 | Args: { 9 | changeset: Record; 10 | isDisabled?: boolean; 11 | isReadOnly?: boolean; 12 | isRequired?: boolean; 13 | isWide?: boolean; 14 | key: string; 15 | label: string; 16 | onUpdate: ({ key, value }: { key: string; value: any }) => void; 17 | placeholder?: string; 18 | }; 19 | } 20 | 21 | export default class UiFormTextareaComponent extends Component { 22 | styles = styles; 23 | 24 | get errorMessage(): string | undefined { 25 | const { isRequired } = this.args; 26 | 27 | return generateErrorMessage({ 28 | isRequired, 29 | value: this.value, 30 | valueType: 'string', 31 | }); 32 | } 33 | 34 | get value(): string { 35 | const { changeset, key } = this.args; 36 | 37 | return ((get(changeset, key) as string) ?? '').toString(); 38 | } 39 | 40 | @action updateValue(event: Event): void { 41 | const { key, onUpdate } = this.args; 42 | const { value } = event.target as HTMLTextAreaElement; 43 | 44 | onUpdate({ key, value }); 45 | } 46 | } 47 | 48 | declare module '@glint/environment-ember-loose/registry' { 49 | export default interface Registry { 50 | 'Ui::Form::Textarea': typeof UiFormTextareaComponent; 51 | 'ui/form/textarea': typeof UiFormTextareaComponent; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/page.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | grid-template-areas: 4 | "title" 5 | "content"; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr; 8 | height: calc(100% - 3em); 9 | overflow-y: auto; 10 | padding: 1.5rem 1rem; 11 | scrollbar-gutter: stable; 12 | } 13 | 14 | .title { 15 | grid-area: title; 16 | } 17 | 18 | .content { 19 | grid-area: content; 20 | } 21 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/page.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'container': string; 3 | readonly 'content': string; 4 | readonly 'title': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/ui/page.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | 3 | import styles from './page.css'; 4 | 5 | interface UiPageSignature { 6 | Args: { 7 | title: string; 8 | }; 9 | Blocks: { 10 | default: []; 11 | }; 12 | } 13 | 14 | const UiPageComponent: TOC = ; 25 | 26 | export default UiPageComponent; 27 | 28 | declare module '@glint/environment-ember-loose/registry' { 29 | export default interface Registry { 30 | 'Ui::Page': typeof UiPageComponent; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-1.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | } 6 | 7 | .items { 8 | display: grid; 9 | flex: 1; 10 | grid-gap: 0.6rem; 11 | overflow: hidden; 12 | } 13 | 14 | .item-1 { 15 | grid-area: item-1; 16 | } 17 | 18 | .item-2 { 19 | grid-area: item-2; 20 | } 21 | 22 | .item-3 { 23 | grid-area: item-3; 24 | } 25 | 26 | .container[data-container-query-tall] .items { 27 | grid-template-areas: 28 | "item-1" 29 | "item-2" 30 | "item-3"; 31 | grid-template-columns: 1fr; 32 | grid-template-rows: repeat(3, 1fr); 33 | } 34 | 35 | .container[data-container-query-square] .items { 36 | grid-template-areas: 37 | "item-1 item-2" 38 | "item-3 item-3"; 39 | grid-template-columns: repeat(2, 1fr); 40 | grid-template-rows: repeat(2, 1fr); 41 | } 42 | 43 | .container[data-container-query-wide] .items { 44 | grid-template-areas: "item-1 item-2 item-3"; 45 | grid-template-columns: repeat(3, 1fr); 46 | grid-template-rows: 1fr; 47 | } 48 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-1.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'container': string; 3 | readonly 'item-1': string; 4 | readonly 'item-2': string; 5 | readonly 'item-3': string; 6 | readonly 'items': string; 7 | }; 8 | 9 | export default styles; 10 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-1.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { hash } from '@ember/helper'; 3 | import { aspectRatio, ContainerQuery } from 'ember-container-query'; 4 | 5 | import styles from './widget-1.css'; 6 | import WidgetsWidget1Item from './widget-1/item'; 7 | 8 | interface WidgetsWidget1Signature { 9 | Args: {}; 10 | } 11 | 12 | const WidgetsWidget1Component: TOC = ; 41 | 42 | export default WidgetsWidget1Component; 43 | 44 | declare module '@glint/environment-ember-loose/registry' { 45 | export default interface Registry { 46 | 'Widgets::Widget-1': typeof WidgetsWidget1Component; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-1/item.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: linear-gradient( 3 | 36deg, 4 | rgb(255 224 130 / 40%) 15%, 5 | rgb(255 248 225 / 80%) 90% 6 | ); 7 | border-radius: 0.1875rem; 8 | height: calc(100% - 1.2rem); 9 | padding: 0.6rem; 10 | width: calc(100% - 1.2rem); 11 | word-break: break-all; 12 | } 13 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-1/item.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'container': string; 3 | }; 4 | 5 | export default styles; 6 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-1/item.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | 3 | import styles from './item.css'; 4 | 5 | interface WidgetsWidget1ItemSignature { 6 | Args: { 7 | title: string; 8 | }; 9 | } 10 | 11 | const WidgetsWidget1ItemComponent: TOC = ; 18 | 19 | export default WidgetsWidget1ItemComponent; 20 | 21 | declare module '@glint/environment-ember-loose/registry' { 22 | export default interface Registry { 23 | 'Widgets::Widget-1::Item': typeof WidgetsWidget1ItemComponent; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | } 6 | 7 | .visualization { 8 | display: flex; 9 | flex: 1; 10 | flex-direction: column; 11 | } 12 | 13 | .container[data-container-query-short] .captions { 14 | flex: 1; 15 | } 16 | 17 | .container[data-container-query-tall] .captions { 18 | height: 10%; 19 | margin-top: 0.6rem; 20 | min-height: 3rem; 21 | } 22 | 23 | .container[data-container-query-very-tall] .captions { 24 | height: 25%; 25 | margin-top: 0.6rem; 26 | max-height: 10rem; 27 | } 28 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'captions': string; 3 | readonly 'container': string; 4 | readonly 'visualization': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2.hbs: -------------------------------------------------------------------------------- 1 | 11 |
12 |

Widget 2

13 |
14 | 15 | {{#unless CQ.features.short}} 16 |
17 | 18 |
19 | {{/unless}} 20 | 21 |
22 | 25 |
26 |
-------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2.ts: -------------------------------------------------------------------------------- 1 | import type Owner from '@ember/owner'; 2 | import Component from '@glimmer/component'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | import { musicRevenues } from '../../data'; 6 | import type { Data, Summary } from '../../utils/components/widgets/widget-2'; 7 | import { 8 | createDataForVisualization, 9 | createSummariesForCaptions, 10 | } from '../../utils/components/widgets/widget-2'; 11 | import styles from './widget-2.css'; 12 | 13 | interface WidgetsWidget2Signature { 14 | Args: {}; 15 | } 16 | 17 | export default class WidgetsWidget2Component extends Component { 18 | @tracked data = [] as Data[]; 19 | @tracked summaries = [] as Summary[]; 20 | 21 | styles = styles; 22 | 23 | constructor(owner: Owner, args: WidgetsWidget2Signature['Args']) { 24 | super(owner, args); 25 | 26 | this.loadData(); 27 | } 28 | 29 | loadData(): void { 30 | this.data = createDataForVisualization(musicRevenues); 31 | this.summaries = createSummariesForCaptions(this.data); 32 | } 33 | } 34 | 35 | declare module '@glint/environment-ember-loose/registry' { 36 | export default interface Registry { 37 | 'Widgets::Widget-2': typeof WidgetsWidget2Component; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2/captions.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'annual-revenue': string; 3 | readonly 'container': string; 4 | readonly 'flat': string; 5 | readonly 'highlight': string; 6 | readonly 'horizontal-layout': string; 7 | readonly 'icon': string; 8 | readonly 'marker': string; 9 | readonly 'music-format': string; 10 | readonly 'next-button': string; 11 | readonly 'previous-button': string; 12 | readonly 'relevant-years': string; 13 | readonly 'small-font-size': string; 14 | readonly 'summary': string; 15 | }; 16 | 17 | export default styles; 18 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2/captions.ts: -------------------------------------------------------------------------------- 1 | import { action } from '@ember/object'; 2 | import Component from '@glimmer/component'; 3 | import { tracked } from '@glimmer/tracking'; 4 | import { modifier } from 'ember-modifier'; 5 | 6 | import type { Summary } from '../../../utils/components/widgets/widget-2'; 7 | import styles from './captions.css'; 8 | 9 | const colorSvg = modifier((container: Element, [color]: [string]) => { 10 | const svgElement = container.querySelector('svg'); 11 | 12 | if (!svgElement) { 13 | return; 14 | } 15 | 16 | svgElement.style.setProperty('color', color); 17 | }); 18 | 19 | interface WidgetsWidget2CaptionsSignature { 20 | Args: { 21 | summaries?: Summary[]; 22 | }; 23 | } 24 | 25 | export default class WidgetsWidget2CaptionsComponent extends Component { 26 | @tracked currentIndex = 0; 27 | 28 | colorSvg = colorSvg; 29 | styles = styles; 30 | 31 | get canShowNextButton(): boolean { 32 | return this.currentIndex < this.summaries.length - 1; 33 | } 34 | 35 | get canShowPreviousButton(): boolean { 36 | return this.currentIndex > 0; 37 | } 38 | 39 | get summaries(): Summary[] { 40 | return this.args.summaries ?? []; 41 | } 42 | 43 | get summary(): Summary | undefined { 44 | return this.summaries[this.currentIndex]; 45 | } 46 | 47 | @action showNextSummary(increment = 1): void { 48 | const { currentIndex, summaries } = this; 49 | 50 | const numSummaries = summaries.length; 51 | const nextIndex = (currentIndex + increment + numSummaries) % numSummaries; 52 | 53 | this.currentIndex = nextIndex; 54 | } 55 | } 56 | 57 | declare module '@glint/environment-ember-loose/registry' { 58 | export default interface Registry { 59 | 'Widgets::Widget-2::Captions': typeof WidgetsWidget2CaptionsComponent; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2/stacked-chart.css: -------------------------------------------------------------------------------- 1 | .svg-container { 2 | background: linear-gradient( 3 | 36deg, 4 | rgb(255 224 130 / 40%) 15%, 5 | rgb(255 248 225 / 80%) 90% 6 | ); 7 | border-radius: 0.1875rem; 8 | height: 100%; 9 | overflow: hidden; 10 | position: relative; 11 | width: 100%; 12 | } 13 | 14 | .svg { 15 | left: 0; 16 | position: absolute; 17 | top: 0; 18 | } 19 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2/stacked-chart.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'svg': string; 3 | readonly 'svg-container': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-2/stacked-chart.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | 3 | // eslint-disable-next-line import-x/default 4 | import drawStackedChart from '../../../modifiers/draw-stacked-chart'; 5 | import type { Data } from '../../../utils/components/widgets/widget-2'; 6 | import styles from './stacked-chart.css'; 7 | 8 | interface WidgetsWidget2StackedChartSignature { 9 | Args: { 10 | data: Data[]; 11 | }; 12 | } 13 | 14 | const WidgetsWidget2StackedChartComponent: TOC = 15 | ; 25 | 26 | export default WidgetsWidget2StackedChartComponent; 27 | 28 | declare module '@glint/environment-ember-loose/registry' { 29 | export default interface Registry { 30 | 'Widgets::Widget-2::StackedChart': typeof WidgetsWidget2StackedChartComponent; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | justify-content: space-between; 10 | } 11 | 12 | .actions { 13 | margin-top: 0.4rem; 14 | } 15 | 16 | .tour-schedule { 17 | display: flex; 18 | flex: 1; 19 | flex-direction: column; 20 | overflow: hidden; 21 | } 22 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'actions': string; 3 | readonly 'container': string; 4 | readonly 'header': string; 5 | readonly 'tour-schedule': string; 6 | }; 7 | 8 | export default styles; 9 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Widget 3

4 | 5 | 10 |
11 | 12 |
16 | 19 |
20 |
-------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3.ts: -------------------------------------------------------------------------------- 1 | import type Owner from '@ember/owner'; 2 | import Component from '@glimmer/component'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | import { type Concert, concert } from '../../data'; 6 | import styles from './widget-3.css'; 7 | 8 | interface WidgetsWidget3Signature { 9 | Args: {}; 10 | } 11 | 12 | export default class WidgetsWidget3Component extends Component { 13 | @tracked concert = {} as Concert; 14 | 15 | styles = styles; 16 | 17 | constructor(owner: Owner, args: WidgetsWidget3Signature['Args']) { 18 | super(owner, args); 19 | 20 | this.loadData(); 21 | } 22 | 23 | loadData(): void { 24 | this.concert = concert; 25 | } 26 | } 27 | 28 | declare module '@glint/environment-ember-loose/registry' { 29 | export default interface Registry { 30 | 'Widgets::Widget-3': typeof WidgetsWidget3Component; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3/tour-schedule.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | grid-template-areas: 4 | "splash" 5 | "."; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: 1fr 0; 8 | height: 100%; 9 | padding: 0 0.5rem; 10 | width: calc(100% - 1rem); 11 | } 12 | 13 | .splash { 14 | grid-area: splash; 15 | height: 100%; 16 | overflow: hidden; 17 | position: relative; 18 | width: 100%; 19 | } 20 | 21 | /* Splash image */ 22 | .splash-image-container { 23 | height: 100%; 24 | width: 100%; 25 | } 26 | 27 | .placeholder-image { 28 | background: linear-gradient( 29 | 36deg, 30 | rgb(255 224 130 / 40%) 15%, 31 | rgb(255 248 225 / 80%) 90% 32 | ); 33 | border-radius: 0.25rem; 34 | height: 100%; 35 | width: 100%; 36 | } 37 | 38 | /* Concert date */ 39 | .concert-date-container { 40 | height: calc(100% - 0.5rem); 41 | left: 0; 42 | padding: 0.25rem 0.5rem; 43 | position: absolute; 44 | top: 0; 45 | width: calc(100% - 1rem); 46 | } 47 | 48 | .concert-date { 49 | font-size: 1.25rem; 50 | font-weight: 700; 51 | } 52 | 53 | /* Venue name */ 54 | .venue-name-container { 55 | align-items: center; 56 | display: flex; 57 | height: 100%; 58 | justify-content: center; 59 | left: 0; 60 | position: absolute; 61 | top: 0; 62 | width: 100%; 63 | } 64 | 65 | .concert-link { 66 | display: block; 67 | font-size: 4rem; 68 | font-style: italic; 69 | font-weight: 700; 70 | height: 100%; 71 | letter-spacing: 0.25rem; 72 | text-decoration: none; 73 | text-transform: uppercase; 74 | width: 100%; 75 | } 76 | 77 | .venue-name { 78 | align-items: center; 79 | display: flex; 80 | height: 100%; 81 | justify-content: center; 82 | width: 100%; 83 | } 84 | 85 | .container[data-cq-small] { 86 | padding: 0 0.25rem; 87 | width: calc(100% - 0.5rem); 88 | } 89 | 90 | .container[data-cq-small] .concert-date { 91 | font-size: 1rem; 92 | font-weight: 400; 93 | } 94 | 95 | .container[data-cq-small] .venue-name { 96 | font-size: 2.25rem; 97 | letter-spacing: 0.125rem; 98 | } 99 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3/tour-schedule.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'concert-date': string; 3 | readonly 'concert-date-container': string; 4 | readonly 'concert-link': string; 5 | readonly 'container': string; 6 | readonly 'placeholder-image': string; 7 | readonly 'splash': string; 8 | readonly 'splash-image-container': string; 9 | readonly 'venue-name': string; 10 | readonly 'venue-name-container': string; 11 | }; 12 | 13 | export default styles; 14 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3/tour-schedule.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { hash } from '@ember/helper'; 3 | import { ContainerQuery, width } from 'ember-container-query'; 4 | 5 | import type { Concert } from '../../../data'; 6 | import styles from './tour-schedule.css'; 7 | import WidgetsWidget3TourScheduleResponsiveImage from './tour-schedule/responsive-image'; 8 | 9 | interface WidgetsWidget3TourScheduleSignature { 10 | Args: { 11 | concert: Concert; 12 | }; 13 | } 14 | 15 | const WidgetsWidget3TourScheduleComponent: TOC = 16 | ; 51 | 52 | export default WidgetsWidget3TourScheduleComponent; 53 | 54 | declare module '@glint/environment-ember-loose/registry' { 55 | export default interface Registry { 56 | 'Widgets::Widget-3::TourSchedule': typeof WidgetsWidget3TourScheduleComponent; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3/tour-schedule/responsive-image.css: -------------------------------------------------------------------------------- 1 | .image-container { 2 | display: grid; 3 | height: 100%; 4 | width: 100%; 5 | } 6 | 7 | .image { 8 | border-radius: 0.25rem; 9 | height: 100%; 10 | object-fit: cover; 11 | width: 100%; 12 | } 13 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3/tour-schedule/responsive-image.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'image': string; 3 | readonly 'image-container': string; 4 | }; 5 | 6 | export default styles; 7 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-3/tour-schedule/responsive-image.gts: -------------------------------------------------------------------------------- 1 | import { action } from '@ember/object'; 2 | import Component from '@glimmer/component'; 3 | import { tracked } from '@glimmer/tracking'; 4 | import { containerQuery, type Dimensions } from 'ember-container-query'; 5 | 6 | import type { ConcertImage } from '../../../../data'; 7 | import { findBestFittingImage } from '../../../../utils/components/widgets/widget-3'; 8 | import styles from './responsive-image.css'; 9 | 10 | interface WidgetsWidget3TourScheduleResponsiveImageSignature { 11 | Args: { 12 | images: ConcertImage[]; 13 | }; 14 | } 15 | 16 | export default class WidgetsWidget3TourScheduleResponsiveImageComponent extends Component { 17 | @tracked imageSource?: string; 18 | 19 | @action setImageSource({ dimensions }: { dimensions: Dimensions }): void { 20 | const { images } = this.args; 21 | 22 | this.imageSource = findBestFittingImage(images, dimensions); 23 | } 24 | 25 | 43 | } 44 | 45 | declare module '@glint/environment-ember-loose/registry' { 46 | export default interface Registry { 47 | 'Widgets::Widget-3::TourSchedule::ResponsiveImage': typeof WidgetsWidget3TourScheduleResponsiveImageComponent; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | grid-template-areas: 4 | "header" 5 | "memo-highlight" 6 | "actions"; 7 | grid-template-columns: 1fr; 8 | grid-template-rows: auto 1fr 2.5rem; 9 | height: 100%; 10 | } 11 | 12 | .header { 13 | grid-area: header; 14 | } 15 | 16 | .memo-highlight { 17 | grid-area: memo-highlight; 18 | overflow: hidden; 19 | } 20 | 21 | .actions { 22 | align-items: flex-end; 23 | display: flex; 24 | grid-area: actions; 25 | justify-content: flex-end; 26 | } 27 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'actions': string; 3 | readonly 'container': string; 4 | readonly 'header': string; 5 | readonly 'memo-highlight': string; 6 | }; 7 | 8 | export default styles; 9 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | 3 | import styles from './widget-4.css'; 4 | import WidgetsWidget4Memo from './widget-4/memo'; 5 | 6 | interface WidgetsWidget4Signature { 7 | Args: {}; 8 | } 9 | 10 | const WidgetsWidget4Component: TOC = ; 27 | 28 | export default WidgetsWidget4Component; 29 | 30 | declare module '@glint/environment-ember-loose/registry' { 31 | export default interface Registry { 32 | 'Widgets::Widget-4': typeof WidgetsWidget4Component; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: linear-gradient( 3 | 36deg, 4 | rgb(179 229 252 / 55%) 35%, 5 | rgb(230 255 255 / 82.5%) 80% 6 | ); 7 | border-radius: 0.1875rem; 8 | color: #455a64; 9 | display: flex; 10 | flex-direction: column; 11 | height: calc(100% - 1.2rem); 12 | padding: 0.6rem; 13 | width: calc(100% - 1.2rem); 14 | } 15 | 16 | .header-container { 17 | margin-bottom: 0.75rem; 18 | } 19 | 20 | .body-container { 21 | flex: 1; 22 | overflow: hidden; 23 | } 24 | 25 | .actions-container { 26 | height: 1.75rem; 27 | margin-top: 0.5rem; 28 | padding: 0 0.5rem; 29 | } 30 | 31 | .container[data-container-query-short] { 32 | height: calc(100% - 1rem); 33 | padding: 0.5rem; 34 | width: calc(100% - 1rem); 35 | } 36 | 37 | .container[data-container-query-short] .header-container { 38 | margin-bottom: 0.5rem; 39 | } 40 | 41 | .container[data-container-query-short] .body-container { 42 | max-height: 9rem; 43 | } 44 | 45 | .container[data-container-query-small] .actions-container, 46 | .container[data-container-query-short] .actions-container { 47 | height: 1rem; 48 | margin-top: 0.25rem; 49 | padding: 0 0.25rem; 50 | } 51 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'actions-container': string; 3 | readonly 'body-container': string; 4 | readonly 'container': string; 5 | readonly 'header-container': string; 6 | }; 7 | 8 | export default styles; 9 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { hash } from '@ember/helper'; 3 | import { ContainerQuery, height, width } from 'ember-container-query'; 4 | 5 | import styles from './memo.css'; 6 | import WidgetsWidget4MemoActions from './memo/actions'; 7 | import WidgetsWidget4MemoBody from './memo/body'; 8 | import WidgetsWidget4MemoHeader from './memo/header'; 9 | 10 | interface WidgetsWidget4MemoSignature { 11 | Args: {}; 12 | } 13 | 14 | const WidgetsWidget4MemoComponent: TOC = ; 38 | 39 | export default WidgetsWidget4MemoComponent; 40 | 41 | declare module '@glint/environment-ember-loose/registry' { 42 | export default interface Registry { 43 | 'Widgets::Widget-4::Memo': typeof WidgetsWidget4MemoComponent; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/actions.css: -------------------------------------------------------------------------------- 1 | .actions { 2 | align-items: center; 3 | display: flex; 4 | justify-content: space-between; 5 | } 6 | 7 | .button { 8 | align-items: center; 9 | border: 0; 10 | border-radius: 50%; 11 | display: flex; 12 | height: 2rem; 13 | justify-content: center; 14 | margin: 0; 15 | padding: 0; 16 | width: 2rem; 17 | } 18 | 19 | .icon { 20 | color: #03a9f4; 21 | height: 1.25rem; 22 | width: 1.25rem; 23 | } 24 | 25 | .icon-comment { 26 | transform: translate(0, 0) scale(-1, 1); 27 | } 28 | 29 | .icon-repost { 30 | transform: translate(0, 0) scale(-1, 1) rotate(90deg); 31 | } 32 | 33 | .actions.minimal-layout .button { 34 | height: 1.25rem; 35 | width: 1.25rem; 36 | } 37 | 38 | .actions.minimal-layout .button:hover { 39 | background: transparent; 40 | transition: none; 41 | } 42 | 43 | .actions.minimal-layout .icon { 44 | height: 0.875rem; 45 | width: 0.875rem; 46 | } 47 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/actions.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'actions': string; 3 | readonly 'button': string; 4 | readonly 'icon': string; 5 | readonly 'icon-comment': string; 6 | readonly 'icon-repost': string; 7 | readonly 'minimal-layout': string; 8 | }; 9 | 10 | export default styles; 11 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/body.css: -------------------------------------------------------------------------------- 1 | .body { 2 | background: linear-gradient( 3 | 126deg, 4 | rgb(238 255 255 / 50%) 20%, 5 | rgb(248 253 255 / 70%) 80% 6 | ); 7 | border-radius: 0.1875rem; 8 | height: 100%; 9 | } 10 | 11 | .message { 12 | font-size: 0.8rem; 13 | height: calc(100% - 1.5rem); 14 | line-height: 1.75; 15 | overflow-y: auto; 16 | padding: 0.75rem; 17 | width: calc(100% - 1.5rem); 18 | } 19 | 20 | .message a { 21 | color: #03a9f4; 22 | } 23 | 24 | .body.minimal-layout .message { 25 | font-size: 0.75rem; 26 | height: calc(100% - 1rem); 27 | line-height: 1.6; 28 | padding: 0.5rem; 29 | width: calc(100% - 1rem); 30 | } 31 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/body.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'body': string; 3 | readonly 'message': string; 4 | readonly 'minimal-layout': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/body.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import type { QueryResults } from 'ember-container-query'; 3 | import { or } from 'ember-truth-helpers'; 4 | import { local } from 'embroider-css-modules'; 5 | 6 | import styles from './body.css'; 7 | 8 | interface WidgetsWidget4MemoBodySignature { 9 | Args: { 10 | cqFeatures?: QueryResults<'small' | 'large' | 'short'>; 11 | }; 12 | } 13 | 14 | const WidgetsWidget4MemoBodyComponent: TOC = 15 | ; 52 | 53 | export default WidgetsWidget4MemoBodyComponent; 54 | 55 | declare module '@glint/environment-ember-loose/registry' { 56 | export default interface Registry { 57 | 'Widgets::Widget-4::Memo::Body': typeof WidgetsWidget4MemoBodyComponent; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/header.css: -------------------------------------------------------------------------------- 1 | .header { 2 | display: grid; 3 | grid-gap: 0.25rem 0; 4 | grid-template-areas: 5 | "avatar-container name" 6 | "avatar-container metadata"; 7 | grid-template-columns: auto 1fr; 8 | grid-template-rows: auto auto; 9 | } 10 | 11 | .avatar-container { 12 | display: grid; 13 | grid-area: avatar-container; 14 | height: 2rem; 15 | margin-right: 0.5rem; 16 | width: 2rem; 17 | } 18 | 19 | .avatar { 20 | border-radius: 50%; 21 | height: 100%; 22 | object-fit: cover; 23 | width: 100%; 24 | } 25 | 26 | .name { 27 | font-size: 0.875rem; 28 | font-weight: 700; 29 | grid-area: name; 30 | margin: 0; 31 | } 32 | 33 | .metadata { 34 | font-size: 0.8rem; 35 | grid-area: metadata; 36 | } 37 | 38 | .handle { 39 | color: #03a9f4; 40 | text-decoration: none; 41 | } 42 | 43 | .header.horizontal-layout { 44 | grid-gap: 0 0.6rem; 45 | grid-template-areas: "name metadata"; 46 | grid-template-columns: 1fr auto; 47 | grid-template-rows: 1fr; 48 | } 49 | 50 | .header.minimal-layout .name { 51 | font-size: 0.8rem; 52 | } 53 | 54 | .header.minimal-layout .metadata { 55 | font-size: 0.75rem; 56 | } 57 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/header.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'avatar': string; 3 | readonly 'avatar-container': string; 4 | readonly 'handle': string; 5 | readonly 'header': string; 6 | readonly 'horizontal-layout': string; 7 | readonly 'metadata': string; 8 | readonly 'minimal-layout': string; 9 | readonly 'name': string; 10 | }; 11 | 12 | export default styles; 13 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-4/memo/header.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import type { QueryResults } from 'ember-container-query'; 3 | import { and, or } from 'ember-truth-helpers'; 4 | import { local } from 'embroider-css-modules'; 5 | 6 | import styles from './header.css'; 7 | 8 | interface WidgetsWidget4MemoHeaderSignature { 9 | Args: { 10 | cqFeatures?: QueryResults<'small' | 'large' | 'short'>; 11 | }; 12 | } 13 | 14 | const WidgetsWidget4MemoHeaderComponent: TOC = 15 | ; 55 | 56 | export default WidgetsWidget4MemoHeaderComponent; 57 | 58 | declare module '@glint/environment-ember-loose/registry' { 59 | export default interface Registry { 60 | 'Widgets::Widget-4::Memo::Header': typeof WidgetsWidget4MemoHeaderComponent; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-5.css: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100%; 3 | overflow: hidden; 4 | } 5 | 6 | .call-to-action { 7 | align-items: center; 8 | display: flex; 9 | flex-direction: column; 10 | font-size: 0.8rem; 11 | height: 100%; 12 | justify-content: center; 13 | line-height: 1.25; 14 | width: 100%; 15 | } 16 | 17 | .call-to-action p { 18 | margin: 0; 19 | } 20 | 21 | .call-to-action .highlight { 22 | font-family: monospace; 23 | } 24 | 25 | .call-to-action a { 26 | border: 0.0625rem solid white; 27 | display: block; 28 | height: calc(100% - 2 * (0.2rem + 0.0625rem)); 29 | padding: 0.2rem 0.5rem; 30 | text-decoration: none; 31 | width: calc(100% - 2 * (0.5rem + 0.0625rem)); 32 | } 33 | 34 | .container[data-container-query-tall] .call-to-action { 35 | font-size: 0.875rem; 36 | line-height: 1.75; 37 | } 38 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-5.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'call-to-action': string; 3 | readonly 'container': string; 4 | readonly 'highlight': string; 5 | }; 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /docs-app/app/components/widgets/widget-5.gts: -------------------------------------------------------------------------------- 1 | import type { TOC } from '@ember/component/template-only'; 2 | import { hash } from '@ember/helper'; 3 | import { ContainerQuery, height, width } from 'ember-container-query'; 4 | import { and } from 'ember-truth-helpers'; 5 | 6 | import styles from './widget-5.css'; 7 | 8 | interface WidgetsWidget5Signature { 9 | Args: {}; 10 | } 11 | 12 | const WidgetsWidget5Component: TOC = ; 42 | 43 | export default WidgetsWidget5Component; 44 | 45 | declare module '@glint/environment-ember-loose/registry' { 46 | export default interface Registry { 47 | 'Widgets::Widget-5': typeof WidgetsWidget5Component; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs-app/app/config/environment.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Type declarations for 3 | * import config from 'my-app/config/environment' 4 | */ 5 | declare const config: { 6 | APP: Record; 7 | environment: string; 8 | locationType: 'history' | 'hash' | 'none' | 'auto'; 9 | modulePrefix: string; 10 | podModulePrefix: string; 11 | rootURL: string; 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /docs-app/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/app/controllers/.gitkeep -------------------------------------------------------------------------------- /docs-app/app/controllers/album.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | grid-template-areas: 4 | "album-summary" 5 | "album-tracks" 6 | "track-lyrics"; 7 | grid-template-columns: 1fr; 8 | grid-template-rows: auto 1fr 0; 9 | } 10 | 11 | .album-summary { 12 | grid-area: album-summary; 13 | } 14 | 15 | .album-tracks { 16 | grid-area: album-tracks; 17 | margin-top: 1rem; 18 | } 19 | 20 | .track-lyrics { 21 | grid-area: track-lyrics; 22 | overflow-y: auto; 23 | padding-right: 1rem; 24 | } 25 | 26 | .track-lyrics .heading-2 { 27 | font-size: 1.25rem; 28 | font-weight: 700; 29 | margin-bottom: 0.75rem; 30 | } 31 | 32 | .track-lyrics .heading-3 { 33 | color: goldenrod; 34 | } 35 | 36 | .track-lyrics .lyrics { 37 | font-size: 0.8rem; 38 | line-height: 1.75; 39 | } 40 | 41 | .with-lyrics { 42 | grid-column-gap: 2rem; 43 | grid-template-areas: 44 | "album-summary track-lyrics" 45 | "album-tracks track-lyrics"; 46 | grid-template-columns: 1fr 18rem; 47 | grid-template-rows: auto 1fr; 48 | overflow-y: hidden; 49 | } 50 | 51 | .with-lyrics .album-tracks { 52 | margin-bottom: 0; 53 | overflow-y: auto; 54 | } 55 | -------------------------------------------------------------------------------- /docs-app/app/controllers/album.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'album-summary': string; 3 | readonly 'album-tracks': string; 4 | readonly 'container': string; 5 | readonly 'heading-2': string; 6 | readonly 'heading-3': string; 7 | readonly 'lyrics': string; 8 | readonly 'track-lyrics': string; 9 | readonly 'with-lyrics': string; 10 | }; 11 | 12 | export default styles; 13 | -------------------------------------------------------------------------------- /docs-app/app/controllers/album.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | import type { Model } from '../routes/album'; 4 | import styles from './album.css'; 5 | 6 | export default class AlbumController extends Controller { 7 | declare model: Model; 8 | styles = styles; 9 | } 10 | -------------------------------------------------------------------------------- /docs-app/app/controllers/application.css: -------------------------------------------------------------------------------- 1 | .application { 2 | display: grid; 3 | grid-template-areas: 4 | ". header ." 5 | "main main main" 6 | ". footer ."; 7 | grid-template-columns: 1fr minmax(auto, 75rem) 1fr; 8 | grid-template-rows: auto 1fr auto; 9 | height: 100%; 10 | overflow: hidden; 11 | width: 100vw; 12 | } 13 | 14 | .header { 15 | grid-area: header; 16 | min-height: 2.75rem; 17 | overflow-x: auto; 18 | } 19 | 20 | .main { 21 | background-color: rgb(255 255 255 / 4.5%); 22 | border-bottom: 0.0625rem solid rgb(211 211 211 / 15%); 23 | border-top: 0.0625rem solid rgb(211 211 211 / 15%); 24 | display: flex; 25 | flex: 1; 26 | grid-area: main; 27 | justify-content: center; 28 | overflow-y: hidden; 29 | } 30 | 31 | .center { 32 | display: flex; 33 | flex-direction: column; 34 | max-width: 75rem; 35 | overflow-y: auto; 36 | width: 100%; 37 | } 38 | 39 | .footer { 40 | align-items: center; 41 | display: flex; 42 | grid-area: footer; 43 | justify-content: center; 44 | min-height: 2.375rem; 45 | } 46 | 47 | .copyright { 48 | color: rgb(128 191 255 / 90%); 49 | font-size: 0.75rem; 50 | padding: 0.75rem 0; 51 | } 52 | 53 | .copyright .link { 54 | color: rgb(128 191 255 / 90%); 55 | } 56 | -------------------------------------------------------------------------------- /docs-app/app/controllers/application.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'application': string; 3 | readonly 'center': string; 4 | readonly 'copyright': string; 5 | readonly 'footer': string; 6 | readonly 'header': string; 7 | readonly 'link': string; 8 | readonly 'main': string; 9 | }; 10 | 11 | export default styles; 12 | -------------------------------------------------------------------------------- /docs-app/app/controllers/application.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | import styles from './application.css'; 4 | 5 | export default class ApplicationController extends Controller { 6 | styles = styles; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/controllers/dashboard.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'widget-1': string; 3 | readonly 'widget-2': string; 4 | readonly 'widget-3': string; 5 | readonly 'widget-4': string; 6 | readonly 'widget-5': string; 7 | readonly 'widgets': string; 8 | }; 9 | 10 | export default styles; 11 | -------------------------------------------------------------------------------- /docs-app/app/controllers/dashboard.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | import styles from './dashboard.css'; 4 | 5 | export default class DashboardController extends Controller { 6 | styles = styles; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/controllers/form.css: -------------------------------------------------------------------------------- 1 | .field { 2 | margin-bottom: 1.25rem; 3 | } 4 | 5 | .field:last-of-type { 6 | margin-bottom: 0; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/controllers/form.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'field': string; 3 | }; 4 | 5 | export default styles; 6 | -------------------------------------------------------------------------------- /docs-app/app/controllers/form.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { action } from '@ember/object'; 3 | 4 | import styles from './form.css'; 5 | 6 | export default class FormController extends Controller { 7 | styles = styles; 8 | 9 | // eslint-disable-next-line @typescript-eslint/require-await 10 | @action async submitForm(data: Record): Promise { 11 | console.table(data); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs-app/app/controllers/index.css: -------------------------------------------------------------------------------- 1 | .code { 2 | font-family: monospace; 3 | } 4 | -------------------------------------------------------------------------------- /docs-app/app/controllers/index.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'code': string; 3 | }; 4 | 5 | export default styles; 6 | -------------------------------------------------------------------------------- /docs-app/app/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | import styles from './index.css'; 4 | 5 | export default class IndexController extends Controller { 6 | styles = styles; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/controllers/not-found.css: -------------------------------------------------------------------------------- 1 | .animation { 2 | flex: 1; 3 | margin: 2rem 0 1.5rem 0.75rem; 4 | } 5 | 6 | .metaphor { 7 | display: grid; 8 | grid-template-columns: repeat(10, 1fr); 9 | grid-template-rows: repeat(3, 1fr); 10 | height: 4.5rem; 11 | width: 15rem; 12 | } 13 | 14 | .mental-block { 15 | animation-duration: 2s; 16 | animation-iteration-count: infinite; 17 | animation-name: extend-right; 18 | background-color: goldenrod; 19 | border-radius: 0.125rem; 20 | grid-column: 1; 21 | grid-row: 1; 22 | height: 1.5rem; 23 | width: 1.5rem; 24 | } 25 | 26 | .the-next-idea { 27 | align-items: center; 28 | border: 0.0625rem solid white; 29 | border-radius: 0.1875rem; 30 | display: flex; 31 | font-family: monospace; 32 | font-size: 0.875rem; 33 | grid-column: 6 / 11; 34 | grid-row: 1 / 4; 35 | height: calc(4.5rem - 2 * (0.2rem + 0.0625rem)); 36 | line-height: 1.25; 37 | padding: 0.2rem 0.5rem; 38 | width: calc(100% - 2 * (0.5rem + 0.0625rem)); 39 | } 40 | 41 | @keyframes extend-right { 42 | from { 43 | width: 1.5rem; 44 | } 45 | 46 | to { 47 | width: 10rem; 48 | } 49 | } 50 | 51 | .metaphor.small-layout { 52 | height: 12.5rem; 53 | margin-left: 0; 54 | width: 12.5rem; 55 | } 56 | 57 | .metaphor.small-layout .mental-block { 58 | animation-name: extend-right-variation-1; 59 | height: 1.25rem; 60 | width: 1.25rem; 61 | } 62 | 63 | .metaphor.small-layout .the-next-idea { 64 | height: calc(3.75rem - 2 * (0.2rem + 0.0625rem)); 65 | padding: 0.2rem 0.3rem; 66 | } 67 | 68 | @keyframes extend-right-variation-1 { 69 | from { 70 | width: 1.5rem; 71 | } 72 | 73 | to { 74 | width: 8rem; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs-app/app/controllers/not-found.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'animation': string; 3 | readonly 'mental-block': string; 4 | readonly 'metaphor': string; 5 | readonly 'small-layout': string; 6 | readonly 'the-next-idea': string; 7 | }; 8 | 9 | export default styles; 10 | -------------------------------------------------------------------------------- /docs-app/app/controllers/not-found.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | import styles from './not-found.css'; 4 | 5 | export default class NotFoundController extends Controller { 6 | styles = styles; 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/app/controllers/products.css: -------------------------------------------------------------------------------- 1 | .shared-layout { 2 | display: grid; 3 | grid-template-areas: 4 | "filters" 5 | "product-details" 6 | "list"; 7 | grid-template-columns: 1fr; 8 | } 9 | 10 | .sticky-container { 11 | position: relative; 12 | } 13 | 14 | .products-with-details { 15 | grid-template-rows: auto auto 1fr; 16 | } 17 | 18 | .products { 19 | grid-template-rows: auto 0 1fr; 20 | } 21 | 22 | .filters { 23 | background-color: #15202d; 24 | border-radius: 0.25rem; 25 | box-shadow: inset 0 0 0.125rem #26313d; 26 | display: flex; 27 | flex-direction: column; 28 | grid-area: filters; 29 | margin-bottom: 2rem; 30 | padding: 0.75rem 1.5rem 1.5rem; 31 | position: sticky; 32 | top: -1.5rem; 33 | z-index: 100; 34 | } 35 | 36 | .filter { 37 | margin-bottom: 1.25rem; 38 | } 39 | 40 | .filter:last-of-type { 41 | margin-bottom: 0; 42 | } 43 | 44 | .list { 45 | display: grid; 46 | grid-gap: 1.5rem 1rem; 47 | grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); 48 | grid-template-rows: 1fr; 49 | } 50 | 51 | .product-details { 52 | grid-area: product-details; 53 | } 54 | 55 | @media screen and (width >= 40rem) { 56 | .products-with-details { 57 | grid-template-areas: 58 | "filters filters" 59 | "list product-details"; 60 | grid-template-columns: 1fr auto; 61 | grid-template-rows: auto 1fr; 62 | } 63 | 64 | .products-with-details .list { 65 | height: max-content; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /docs-app/app/controllers/products.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly 'filter': string; 3 | readonly 'filters': string; 4 | readonly 'list': string; 5 | readonly 'product-details': string; 6 | readonly 'products': string; 7 | readonly 'products-with-details': string; 8 | readonly 'shared-layout': string; 9 | readonly 'sticky-container': string; 10 | }; 11 | 12 | export default styles; 13 | -------------------------------------------------------------------------------- /docs-app/app/controllers/products.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | import type { Model } from '../routes/products'; 6 | import styles from './products.css'; 7 | 8 | export default class ProductsController extends Controller { 9 | declare model: Model; 10 | 11 | @tracked name: string | null = null; 12 | 13 | styles = styles; 14 | 15 | get filteredProducts() { 16 | const { model: products, name } = this; 17 | 18 | if (!name) { 19 | return products; 20 | } 21 | 22 | const target = name.toLowerCase(); 23 | 24 | return products.filter((product) => { 25 | const productName = (product.name ?? '').toLowerCase(); 26 | 27 | return productName.includes(target); 28 | }); 29 | } 30 | 31 | get isPartOfNestProductDetailsExperiment() { 32 | return true; 33 | } 34 | 35 | @action updateQueryParameters({ key, value }: { key: string; value: any }) { 36 | if (key !== 'name') { 37 | return; 38 | } 39 | 40 | if (value === undefined || value === '') { 41 | this[key] = null; 42 | 43 | return; 44 | } 45 | 46 | this[key] = value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs-app/app/data/album.ts: -------------------------------------------------------------------------------- 1 | export type Track = { 2 | isExplicit: boolean; 3 | length: string; 4 | name: string; 5 | }; 6 | 7 | export type Album = { 8 | band: { 9 | name: string; 10 | }; 11 | name: string; 12 | publicationDate: string; 13 | totalLengthInMinutes: number; 14 | tracks: Track[]; 15 | }; 16 | 17 | export const album: Album = { 18 | name: 'How to Be a Human Being', 19 | publicationDate: '2016', 20 | totalLengthInMinutes: 43, 21 | band: { 22 | name: 'Glass Animals', 23 | }, 24 | tracks: [ 25 | { 26 | name: 'Life Itself', 27 | length: '4:41', 28 | isExplicit: false, 29 | }, 30 | { 31 | name: 'Youth', 32 | length: '3:51', 33 | isExplicit: false, 34 | }, 35 | { 36 | name: 'Season 2 Episode 3', 37 | length: '4:04', 38 | isExplicit: false, 39 | }, 40 | { 41 | name: 'Pork Soda', 42 | length: '4:14', 43 | isExplicit: true, 44 | }, 45 | { 46 | name: "Mama's Gun", 47 | length: '4:27', 48 | isExplicit: false, 49 | }, 50 | { 51 | name: 'Cane Shuga', 52 | length: '3:17', 53 | isExplicit: false, 54 | }, 55 | { 56 | name: '[Premade Sandwiches]', 57 | length: '0:36', 58 | isExplicit: true, 59 | }, 60 | { 61 | name: 'The Other Side of Paradise', 62 | length: '5:21', 63 | isExplicit: true, 64 | }, 65 | { 66 | name: 'Take a Slice', 67 | length: '3:50', 68 | isExplicit: true, 69 | }, 70 | { 71 | name: 'Poplar St', 72 | length: '4:23', 73 | isExplicit: false, 74 | }, 75 | { 76 | name: 'Agnes', 77 | length: '4:32', 78 | isExplicit: true, 79 | }, 80 | ], 81 | }; 82 | -------------------------------------------------------------------------------- /docs-app/app/data/concert.ts: -------------------------------------------------------------------------------- 1 | export type ConcertImage = { 2 | metadata: { 3 | height: number; 4 | width: number; 5 | }; 6 | url: string; 7 | }; 8 | 9 | export type Concert = { 10 | date: string; 11 | images: ConcertImage[]; 12 | location: { 13 | city: string; 14 | state: string; 15 | }; 16 | name: string; 17 | }; 18 | 19 | export const concert: Concert = { 20 | name: 'ACL Live', 21 | date: 'Jun 01', 22 | location: { 23 | city: 'Austin', 24 | state: 'TX', 25 | }, 26 | images: [ 27 | { 28 | url: '/images/widgets/widget-3/venue-extra-wide@1x.jpg', 29 | metadata: { 30 | height: 150, 31 | width: 540, 32 | }, 33 | }, 34 | { 35 | url: '/images/widgets/widget-3/venue-extra-wide@2x.jpg', 36 | metadata: { 37 | height: 300, 38 | width: 1080, 39 | }, 40 | }, 41 | { 42 | url: '/images/widgets/widget-3/venue-extra-wide@4x.jpg', 43 | metadata: { 44 | height: 600, 45 | width: 2160, 46 | }, 47 | }, 48 | { 49 | url: '/images/widgets/widget-3/venue-square@1x.jpg', 50 | metadata: { 51 | height: 150, 52 | width: 150, 53 | }, 54 | }, 55 | { 56 | url: '/images/widgets/widget-3/venue-square@2x.jpg', 57 | metadata: { 58 | height: 300, 59 | width: 300, 60 | }, 61 | }, 62 | { 63 | url: '/images/widgets/widget-3/venue-square@4x.jpg', 64 | metadata: { 65 | height: 600, 66 | width: 600, 67 | }, 68 | }, 69 | { 70 | url: '/images/widgets/widget-3/venue-wide@1x.jpg', 71 | metadata: { 72 | height: 150, 73 | width: 300, 74 | }, 75 | }, 76 | { 77 | url: '/images/widgets/widget-3/venue-wide@2x.jpg', 78 | metadata: { 79 | height: 300, 80 | width: 600, 81 | }, 82 | }, 83 | { 84 | url: '/images/widgets/widget-3/venue-wide@4x.jpg', 85 | metadata: { 86 | height: 600, 87 | width: 1200, 88 | }, 89 | }, 90 | ], 91 | }; 92 | -------------------------------------------------------------------------------- /docs-app/app/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from './album'; 2 | export * from './concert'; 3 | export * from './music-revenue'; 4 | export * from './products'; 5 | -------------------------------------------------------------------------------- /docs-app/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/app/helpers/.gitkeep -------------------------------------------------------------------------------- /docs-app/app/helpers/add.ts: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | 3 | interface AddHelperSignature { 4 | Args: { 5 | Positional: number[]; 6 | }; 7 | Return: number; 8 | } 9 | 10 | const AddHelper = helper((positional) => { 11 | const sum = positional.reduce((accumulator, value) => accumulator + value, 0); 12 | 13 | return sum; 14 | }); 15 | 16 | export default AddHelper; 17 | 18 | declare module '@glint/environment-ember-loose/registry' { 19 | export default interface Registry { 20 | add: typeof AddHelper; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ember Container Query 10 | 11 | {{content-for "head"}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{content-for "head-footer"}} 20 | 21 | 22 | {{content-for "body"}} 23 | 24 | 25 | 26 | 27 | {{content-for "body-footer"}} 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs-app/app/modifiers/draw-stacked-chart.d.ts: -------------------------------------------------------------------------------- 1 | import type { ModifierLike } from '@glint/template'; 2 | 3 | import type { Data } from '../utils/components/widgets/widget-2'; 4 | 5 | declare module '@glint/environment-ember-loose/registry' { 6 | export default interface Registry { 7 | 'draw-stacked-chart': ModifierLike<{ 8 | Args: { 9 | Named: { 10 | data?: Data[]; 11 | }; 12 | }; 13 | Element: Element; 14 | }>; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs-app/app/modifiers/dynamic-css-grid.ts: -------------------------------------------------------------------------------- 1 | import { modifier } from 'ember-modifier'; 2 | 3 | interface DynamicCssGridModifierSignature { 4 | Args: { 5 | Named: { 6 | numColumns: number; 7 | numRows: number; 8 | }; 9 | Positional: []; 10 | }; 11 | Element: HTMLElement; 12 | } 13 | 14 | const DynamicCssGridModifier = modifier( 15 | (element, _positional, named) => { 16 | const { numColumns, numRows } = named; 17 | 18 | element.style.gridTemplateColumns = `repeat(${numColumns}, minmax(0, 1fr))`; 19 | element.style.gridTemplateRows = `repeat(${numRows}, 1fr)`; 20 | }, 21 | ); 22 | 23 | export default DynamicCssGridModifier; 24 | 25 | declare module '@glint/environment-ember-loose/registry' { 26 | export default interface Registry { 27 | 'dynamic-css-grid': typeof DynamicCssGridModifier; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs-app/app/router.ts: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@embroider/router'; 2 | 3 | import config from './config/environment'; 4 | 5 | export default class Router extends EmberRouter { 6 | location = config.locationType; 7 | rootURL = config.rootURL; 8 | } 9 | 10 | Router.map(function () { 11 | this.route('album'); 12 | this.route('dashboard'); 13 | this.route('form'); 14 | this.route('products'); 15 | 16 | this.route('not-found', { path: '*' }); 17 | }); 18 | -------------------------------------------------------------------------------- /docs-app/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/app/routes/.gitkeep -------------------------------------------------------------------------------- /docs-app/app/routes/album.ts: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | import { type Album, album } from '../data'; 4 | import type { ModelFrom } from '../utils/routes'; 5 | 6 | export default class AlbumRoute extends Route { 7 | model(): Album { 8 | return album; 9 | } 10 | } 11 | 12 | export type Model = ModelFrom; 13 | -------------------------------------------------------------------------------- /docs-app/app/routes/products.ts: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | import { type Product, products } from '../data'; 4 | import type { ModelFrom } from '../utils/routes'; 5 | 6 | export default class ProductsRoute extends Route { 7 | model(): Product[] { 8 | return products; 9 | } 10 | } 11 | 12 | export type Model = ModelFrom; 13 | -------------------------------------------------------------------------------- /docs-app/app/styles/app.css: -------------------------------------------------------------------------------- 1 | /* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */ 2 | -------------------------------------------------------------------------------- /docs-app/app/styles/app.css.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: Record; 2 | 3 | export default styles; 4 | -------------------------------------------------------------------------------- /docs-app/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Ember Container Query"}} 2 | 3 |
4 |
5 | 6 | 7 | 17 |
18 | 19 |
20 |
21 | {{outlet}} 22 |
23 |
24 | 25 | 39 |
-------------------------------------------------------------------------------- /docs-app/app/templates/dashboard.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Dashboard"}} 2 | 3 | 4 |
5 |
6 | 7 |
8 | 9 |
10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 |
25 |
-------------------------------------------------------------------------------- /docs-app/app/templates/form.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Form"}} 2 | 3 | 4 | 17 |
18 | 24 |
25 | 26 |
27 | 34 |
35 | 36 |
37 | 38 |
39 | 40 |
41 | 45 |
46 | 47 |
48 | 55 |
56 |
57 |
-------------------------------------------------------------------------------- /docs-app/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 | 2 |

3 | Thanks for trying out 4 | 11 | ember-container-query 12 | . 13 |

14 | 15 |

16 | To see what you can do with container queries, visit one of 17 | the examples and resize the window! 18 |

19 |
-------------------------------------------------------------------------------- /docs-app/app/templates/not-found.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Page Not Found"}} 2 | 3 | 4 |

Feeling lost? Uncontained?

5 |

Don't worry. We all have our off days.

6 | 7 |
8 | 12 |
19 |
20 |
21 | 22 | 28 |
29 |
30 |
31 |
-------------------------------------------------------------------------------- /docs-app/app/templates/products.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Products"}} 2 | 3 | 4 |
15 |
16 |
17 | 24 |
25 |
26 | 27 |
28 | {{#each this.filteredProducts as |product|}} 29 | 30 | {{else}} 31 |

32 | No products found. 33 |

34 | {{/each}} 35 |
36 | 37 |
38 | {{outlet}} 39 |
40 |
41 |
-------------------------------------------------------------------------------- /docs-app/app/utils/components/ui/form/index.ts: -------------------------------------------------------------------------------- 1 | type Options = { 2 | isRequired?: boolean; 3 | value: any; 4 | valueType: 'boolean' | 'number' | 'string'; 5 | }; 6 | 7 | export function generateErrorMessage({ 8 | isRequired, 9 | value, 10 | valueType, 11 | }: Options): string | undefined { 12 | if (isRequired) { 13 | switch (valueType) { 14 | case 'boolean': { 15 | if (value === undefined || value === false) { 16 | return 'Please select the checkbox.'; 17 | } 18 | 19 | break; 20 | } 21 | 22 | case 'number': 23 | case 'string': { 24 | if (value === undefined || value === '') { 25 | return 'Please provide a value.'; 26 | } 27 | 28 | break; 29 | } 30 | } 31 | } 32 | 33 | return undefined; 34 | } 35 | -------------------------------------------------------------------------------- /docs-app/app/utils/components/widgets/widget-3.ts: -------------------------------------------------------------------------------- 1 | import type { ConcertImage } from '../../../data/concert'; 2 | 3 | export type ContainerDimensions = { 4 | aspectRatio: number; 5 | height: number; 6 | width: number; 7 | }; 8 | 9 | /* 10 | This recommendation system makes 3 assumptions: 11 | 12 | - Users prefer images whose aspect ratio is close to the container's. 13 | - Users prefer images whose height and width are larger than the container's. 14 | - If all images are smaller than the container, users want the image that is 15 | the largest of all. In other words, that image's height and width match the 16 | container's the closest. 17 | */ 18 | export function findBestFittingImage( 19 | images: ConcertImage[], 20 | containerDimensions: ContainerDimensions, 21 | ): string | undefined { 22 | if (images.length === 0) { 23 | return; 24 | } 25 | 26 | const { aspectRatio, height, width } = containerDimensions; 27 | 28 | const imagesRanked = images 29 | .map((image) => { 30 | const { url, metadata } = image; 31 | 32 | const imageHeight = metadata.height; 33 | const imageWidth = metadata.width; 34 | const imageAspectRatio = imageWidth / imageHeight; 35 | 36 | const arMetric = Math.abs(imageAspectRatio - aspectRatio); 37 | const hwMetric = 38 | ((imageHeight - height) ** 3 + (imageWidth - width) ** 3) ** (1 / 3); 39 | const hwTiebreaker = 40 | ((imageHeight - height) ** 2 + (imageWidth - width) ** 2) ** (1 / 2); 41 | 42 | return { 43 | url, 44 | arMetric, 45 | hwMetric: Number.isNaN(hwMetric) ? Infinity : hwMetric, 46 | hwTiebreaker, 47 | }; 48 | }) 49 | .sort((a, b) => { 50 | if (a.arMetric > b.arMetric) { 51 | return 1; 52 | } 53 | 54 | if (a.arMetric < b.arMetric) { 55 | return -1; 56 | } 57 | 58 | if (a.hwMetric > b.hwMetric) { 59 | return 1; 60 | } 61 | 62 | if (a.hwMetric < b.hwMetric) { 63 | return -1; 64 | } 65 | 66 | return a.hwTiebreaker - b.hwTiebreaker; 67 | }); 68 | 69 | return imagesRanked[0]!.url; 70 | } 71 | -------------------------------------------------------------------------------- /docs-app/app/utils/routes/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | https://docs.ember-cli-typescript.com/cookbook/working-with-route-models 3 | */ 4 | import type Route from '@ember/routing/route'; 5 | 6 | /** 7 | Get the resolved type of an item. 8 | - If the item is a promise, the result will be the resolved value type 9 | - If the item is not a promise, the result will just be the type of the item 10 | */ 11 | type Resolved

= P extends Promise ? T : P; 12 | 13 | /** Get the resolved model value from a route. */ 14 | export type ModelFrom = Resolved>; 15 | -------------------------------------------------------------------------------- /docs-app/config/dependency-lint.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | allowedVersions: {}, 3 | }; 4 | -------------------------------------------------------------------------------- /docs-app/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "6.4.0", 7 | "blueprints": [ 8 | { 9 | "name": "app", 10 | "outputRepo": "https://github.com/ember-cli/ember-new-output", 11 | "codemodsSource": "ember-app-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": ["--embroider", "--typescript"] 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /docs-app/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'docs-app', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'history', 9 | EmberENV: { 10 | EXTEND_PROTOTYPES: false, 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 14 | }, 15 | }, 16 | 17 | APP: { 18 | // Here you can pass flags/options to your application instance 19 | // when it is created 20 | }, 21 | }; 22 | 23 | if (environment === 'development') { 24 | // ENV.APP.LOG_RESOLVER = true; 25 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 26 | // ENV.APP.LOG_TRANSITIONS = true; 27 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 28 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 29 | } 30 | 31 | if (environment === 'test') { 32 | // Testem prefers this... 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | ENV.APP.autoboot = false; 41 | } 42 | 43 | if (environment === 'production') { 44 | // here you can enable a production-specific feature 45 | } 46 | 47 | return ENV; 48 | }; 49 | -------------------------------------------------------------------------------- /docs-app/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "no-implicit-route-model": true, 6 | "template-only-glimmer-components": true 7 | } 8 | -------------------------------------------------------------------------------- /docs-app/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | browsers: [ 5 | 'last 2 Chrome versions', 6 | 'last 2 Edge versions', 7 | 'last 2 Firefox versions', 8 | 'last 2 Safari versions', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /docs-app/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfiguration from '@ijlee2-frontend-configs/eslint-config-ember/v1-app'; 2 | 3 | export default [ 4 | ...baseConfiguration, 5 | { 6 | files: ['**/*.{gts,ts}'], 7 | rules: { 8 | '@typescript-eslint/no-explicit-any': 'off', 9 | '@typescript-eslint/no-unsafe-assignment': 'off', 10 | }, 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /docs-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | const env = process.env.EMBER_ENV || 'development'; 2 | 3 | const plugins = [require('autoprefixer')]; 4 | 5 | if (env === 'production') { 6 | // ... 7 | } 8 | 9 | module.exports = { 10 | plugins, 11 | }; 12 | -------------------------------------------------------------------------------- /docs-app/prettier.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@ijlee2-frontend-configs/prettier/ember'; 2 | -------------------------------------------------------------------------------- /docs-app/public/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/assets/favicon.png -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-extra-wide@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-extra-wide@1x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-extra-wide@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-extra-wide@2x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-extra-wide@4x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-extra-wide@4x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-square@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-square@1x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-square@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-square@2x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-square@4x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-square@4x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-wide@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-wide@1x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-wide@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-wide@2x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-3/venue-wide@4x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-3/venue-wide@4x.jpg -------------------------------------------------------------------------------- /docs-app/public/images/widgets/widget-4/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/public/images/widgets/widget-4/avatar.jpg -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/alpha-e-box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/heart-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/message-processing-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/share-variant-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/material-design-icons/sync.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /docs-app/tests/acceptance/album/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { setupApplicationTest, timeout } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Acceptance | album', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(async function () { 10 | await visit('/album'); 11 | await timeout(); 12 | }); 13 | 14 | test('@w1 @h1 Accessibility audit', async function (assert) { 15 | await a11yAudit(); 16 | 17 | assert.ok(true, 'We passed Axe tests.'); 18 | }); 19 | 20 | test('@w2 @h1 Accessibility audit', async function (assert) { 21 | await a11yAudit(); 22 | 23 | assert.ok(true, 'We passed Axe tests.'); 24 | }); 25 | 26 | test('@w3 @h1 Accessibility audit', async function (assert) { 27 | await a11yAudit(); 28 | 29 | assert.ok(true, 'We passed Axe tests.'); 30 | }); 31 | 32 | test('@w1 @h2 Accessibility audit', async function (assert) { 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed Axe tests.'); 36 | }); 37 | 38 | test('@w2 @h2 Accessibility audit', async function (assert) { 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed Axe tests.'); 42 | }); 43 | 44 | test('@w3 @h2 Accessibility audit', async function (assert) { 45 | await a11yAudit(); 46 | 47 | assert.ok(true, 'We passed Axe tests.'); 48 | }); 49 | 50 | test('@w1 @h3 Accessibility audit', async function (assert) { 51 | await a11yAudit(); 52 | 53 | assert.ok(true, 'We passed Axe tests.'); 54 | }); 55 | 56 | test('@w2 @h3 Accessibility audit', async function (assert) { 57 | await a11yAudit(); 58 | 59 | assert.ok(true, 'We passed Axe tests.'); 60 | }); 61 | 62 | test('@w3 @h3 Accessibility audit', async function (assert) { 63 | await a11yAudit(); 64 | 65 | assert.ok(true, 'We passed Axe tests.'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /docs-app/tests/acceptance/dashboard/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { setupApplicationTest, timeout } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Acceptance | dashboard', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(async function () { 10 | await visit('/dashboard'); 11 | await timeout(); 12 | }); 13 | 14 | test('@w1 @h1 Accessibility audit', async function (assert) { 15 | await a11yAudit(); 16 | 17 | assert.ok(true, 'We passed Axe tests.'); 18 | }); 19 | 20 | test('@w2 @h1 Accessibility audit', async function (assert) { 21 | await a11yAudit(); 22 | 23 | assert.ok(true, 'We passed Axe tests.'); 24 | }); 25 | 26 | test('@w3 @h1 Accessibility audit', async function (assert) { 27 | await a11yAudit(); 28 | 29 | assert.ok(true, 'We passed Axe tests.'); 30 | }); 31 | 32 | test('@w1 @h2 Accessibility audit', async function (assert) { 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed Axe tests.'); 36 | }); 37 | 38 | test('@w2 @h2 Accessibility audit', async function (assert) { 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed Axe tests.'); 42 | }); 43 | 44 | test('@w3 @h2 Accessibility audit', async function (assert) { 45 | await a11yAudit(); 46 | 47 | assert.ok(true, 'We passed Axe tests.'); 48 | }); 49 | 50 | test('@w1 @h3 Accessibility audit', async function (assert) { 51 | await a11yAudit(); 52 | 53 | assert.ok(true, 'We passed Axe tests.'); 54 | }); 55 | 56 | test('@w2 @h3 Accessibility audit', async function (assert) { 57 | await a11yAudit(); 58 | 59 | assert.ok(true, 'We passed Axe tests.'); 60 | }); 61 | 62 | test('@w3 @h3 Accessibility audit', async function (assert) { 63 | await a11yAudit(); 64 | 65 | assert.ok(true, 'We passed Axe tests.'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /docs-app/tests/acceptance/form/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { setupApplicationTest, timeout } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Acceptance | form', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(async function () { 10 | await visit('/form'); 11 | await timeout(); 12 | }); 13 | 14 | test('@w1 @h1 Accessibility audit', async function (assert) { 15 | await a11yAudit(); 16 | 17 | assert.ok(true, 'We passed Axe tests.'); 18 | }); 19 | 20 | test('@w2 @h1 Accessibility audit', async function (assert) { 21 | await a11yAudit(); 22 | 23 | assert.ok(true, 'We passed Axe tests.'); 24 | }); 25 | 26 | test('@w3 @h1 Accessibility audit', async function (assert) { 27 | await a11yAudit(); 28 | 29 | assert.ok(true, 'We passed Axe tests.'); 30 | }); 31 | 32 | test('@w1 @h2 Accessibility audit', async function (assert) { 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed Axe tests.'); 36 | }); 37 | 38 | test('@w2 @h2 Accessibility audit', async function (assert) { 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed Axe tests.'); 42 | }); 43 | 44 | test('@w3 @h2 Accessibility audit', async function (assert) { 45 | await a11yAudit(); 46 | 47 | assert.ok(true, 'We passed Axe tests.'); 48 | }); 49 | 50 | test('@w1 @h3 Accessibility audit', async function (assert) { 51 | await a11yAudit(); 52 | 53 | assert.ok(true, 'We passed Axe tests.'); 54 | }); 55 | 56 | test('@w2 @h3 Accessibility audit', async function (assert) { 57 | await a11yAudit(); 58 | 59 | assert.ok(true, 'We passed Axe tests.'); 60 | }); 61 | 62 | test('@w3 @h3 Accessibility audit', async function (assert) { 63 | await a11yAudit(); 64 | 65 | assert.ok(true, 'We passed Axe tests.'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /docs-app/tests/acceptance/index/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { setupApplicationTest, timeout } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Acceptance | index', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(async function () { 10 | await visit('/'); 11 | await timeout(); 12 | }); 13 | 14 | test('@w1 @h1 Accessibility audit', async function (assert) { 15 | await a11yAudit(); 16 | 17 | assert.ok(true, 'We passed Axe tests.'); 18 | }); 19 | 20 | test('@w2 @h1 Accessibility audit', async function (assert) { 21 | await a11yAudit(); 22 | 23 | assert.ok(true, 'We passed Axe tests.'); 24 | }); 25 | 26 | test('@w3 @h1 Accessibility audit', async function (assert) { 27 | await a11yAudit(); 28 | 29 | assert.ok(true, 'We passed Axe tests.'); 30 | }); 31 | 32 | test('@w1 @h2 Accessibility audit', async function (assert) { 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed Axe tests.'); 36 | }); 37 | 38 | test('@w2 @h2 Accessibility audit', async function (assert) { 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed Axe tests.'); 42 | }); 43 | 44 | test('@w3 @h2 Accessibility audit', async function (assert) { 45 | await a11yAudit(); 46 | 47 | assert.ok(true, 'We passed Axe tests.'); 48 | }); 49 | 50 | test('@w1 @h3 Accessibility audit', async function (assert) { 51 | await a11yAudit(); 52 | 53 | assert.ok(true, 'We passed Axe tests.'); 54 | }); 55 | 56 | test('@w2 @h3 Accessibility audit', async function (assert) { 57 | await a11yAudit(); 58 | 59 | assert.ok(true, 'We passed Axe tests.'); 60 | }); 61 | 62 | test('@w3 @h3 Accessibility audit', async function (assert) { 63 | await a11yAudit(); 64 | 65 | assert.ok(true, 'We passed Axe tests.'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /docs-app/tests/acceptance/not-found/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { setupApplicationTest, timeout } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Acceptance | not-found', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(async function () { 10 | await visit('/404'); 11 | await timeout(); 12 | }); 13 | 14 | test('@w1 @h1 Accessibility audit', async function (assert) { 15 | await a11yAudit(); 16 | 17 | assert.ok(true, 'We passed Axe tests.'); 18 | }); 19 | 20 | test('@w2 @h1 Accessibility audit', async function (assert) { 21 | await a11yAudit(); 22 | 23 | assert.ok(true, 'We passed Axe tests.'); 24 | }); 25 | 26 | test('@w3 @h1 Accessibility audit', async function (assert) { 27 | await a11yAudit(); 28 | 29 | assert.ok(true, 'We passed Axe tests.'); 30 | }); 31 | 32 | test('@w1 @h2 Accessibility audit', async function (assert) { 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed Axe tests.'); 36 | }); 37 | 38 | test('@w2 @h2 Accessibility audit', async function (assert) { 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed Axe tests.'); 42 | }); 43 | 44 | test('@w3 @h2 Accessibility audit', async function (assert) { 45 | await a11yAudit(); 46 | 47 | assert.ok(true, 'We passed Axe tests.'); 48 | }); 49 | 50 | test('@w1 @h3 Accessibility audit', async function (assert) { 51 | await a11yAudit(); 52 | 53 | assert.ok(true, 'We passed Axe tests.'); 54 | }); 55 | 56 | test('@w2 @h3 Accessibility audit', async function (assert) { 57 | await a11yAudit(); 58 | 59 | assert.ok(true, 'We passed Axe tests.'); 60 | }); 61 | 62 | test('@w3 @h3 Accessibility audit', async function (assert) { 63 | await a11yAudit(); 64 | 65 | assert.ok(true, 'We passed Axe tests.'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /docs-app/tests/acceptance/products/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { setupApplicationTest, timeout } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Acceptance | products', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(async function () { 10 | await visit('/products'); 11 | await timeout(); 12 | }); 13 | 14 | test('@w1 @h1 Accessibility audit', async function (assert) { 15 | await a11yAudit(); 16 | 17 | assert.ok(true, 'We passed Axe tests.'); 18 | }); 19 | 20 | test('@w2 @h1 Accessibility audit', async function (assert) { 21 | await a11yAudit(); 22 | 23 | assert.ok(true, 'We passed Axe tests.'); 24 | }); 25 | 26 | test('@w3 @h1 Accessibility audit', async function (assert) { 27 | await a11yAudit(); 28 | 29 | assert.ok(true, 'We passed Axe tests.'); 30 | }); 31 | 32 | test('@w1 @h2 Accessibility audit', async function (assert) { 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed Axe tests.'); 36 | }); 37 | 38 | test('@w2 @h2 Accessibility audit', async function (assert) { 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed Axe tests.'); 42 | }); 43 | 44 | test('@w3 @h2 Accessibility audit', async function (assert) { 45 | await a11yAudit(); 46 | 47 | assert.ok(true, 'We passed Axe tests.'); 48 | }); 49 | 50 | test('@w1 @h3 Accessibility audit', async function (assert) { 51 | await a11yAudit(); 52 | 53 | assert.ok(true, 'We passed Axe tests.'); 54 | }); 55 | 56 | test('@w2 @h3 Accessibility audit', async function (assert) { 57 | await a11yAudit(); 58 | 59 | assert.ok(true, 'We passed Axe tests.'); 60 | }); 61 | 62 | test('@w3 @h3 Accessibility audit', async function (assert) { 63 | await a11yAudit(); 64 | 65 | assert.ok(true, 'We passed Axe tests.'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /docs-app/tests/helpers/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | setupApplicationTest as upstreamSetupApplicationTest, 3 | setupRenderingTest as upstreamSetupRenderingTest, 4 | setupTest as upstreamSetupTest, 5 | type SetupTestOptions, 6 | } from 'ember-qunit'; 7 | 8 | import { resetViewport } from './reset-viewport'; 9 | 10 | // This file exists to provide wrappers around ember-qunit's 11 | // test setup functions. This way, you can easily extend the setup that is 12 | // needed per test type. 13 | 14 | function setupApplicationTest( 15 | hooks: NestedHooks, 16 | options?: SetupTestOptions, 17 | ): void { 18 | upstreamSetupApplicationTest(hooks, options); 19 | resetViewport(hooks); 20 | 21 | // Additional setup for application tests can be done here. 22 | // 23 | // For example, if you need an authenticated session for each 24 | // application test, you could do: 25 | // 26 | // hooks.beforeEach(async function () { 27 | // await authenticateSession(); // ember-simple-auth 28 | // }); 29 | // 30 | // This is also a good place to call test setup functions coming 31 | // from other addons: 32 | // 33 | // setupIntl(hooks, 'en-us'); // ember-intl 34 | // setupMirage(hooks); // ember-cli-mirage 35 | } 36 | 37 | function setupRenderingTest( 38 | hooks: NestedHooks, 39 | options?: SetupTestOptions, 40 | ): void { 41 | upstreamSetupRenderingTest(hooks, options); 42 | 43 | // Additional setup for rendering tests can be done here. 44 | } 45 | 46 | function setupTest(hooks: NestedHooks, options?: SetupTestOptions): void { 47 | upstreamSetupTest(hooks, options); 48 | 49 | // Additional setup for unit tests can be done here. 50 | } 51 | 52 | export { setupApplicationTest, setupRenderingTest, setupTest }; 53 | 54 | export * from './percy'; 55 | export * from './reset-viewport'; 56 | export * from './resize-container'; 57 | -------------------------------------------------------------------------------- /docs-app/tests/helpers/reset-viewport.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Do not allow ember-qunit to resize the viewport! Resizing the viewport causes 3 | issues with tests and Percy snapshots for this addon. 4 | 5 | https://github.com/emberjs/ember-qunit/blob/v8.1.0/addon/src/test-container-styles.css#L33 6 | */ 7 | export function resetViewport(hooks: NestedHooks): void { 8 | hooks.beforeEach(function () { 9 | const testingContainer = document.getElementById( 10 | 'ember-testing-container', 11 | )!; 12 | 13 | testingContainer.classList.add('ember-testing-container-full-screen'); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /docs-app/tests/helpers/resize-container.ts: -------------------------------------------------------------------------------- 1 | import { assert } from '@ember/debug'; 2 | import { later } from '@ember/runloop'; 3 | import { find } from '@ember/test-helpers'; 4 | 5 | // This is a magic number. It is the time (in ms) for things to `settle` 6 | // after a resize. It is the time that we need to wait before assertions 7 | // that should pass will always pass. 8 | const RERENDER_TIME = 50; 9 | 10 | export function timeout(milliseconds = RERENDER_TIME): Promise { 11 | return new Promise((resolve) => { 12 | // @ts-expect-error: Did type definition for `later` change in ember-source@5.1? 13 | // eslint-disable-next-line ember/no-runloop 14 | later(resolve, milliseconds); 15 | }); 16 | } 17 | 18 | export async function resizeContainer({ 19 | height, 20 | width, 21 | }: { 22 | height: number; 23 | width: number; 24 | }): Promise { 25 | const parentElement = find('[data-test-parent-element]'); 26 | 27 | assert( 28 | 'Please create a parent element with the test selector `data-test-parent-element`.', 29 | parentElement, 30 | ); 31 | 32 | // Since has a style of `height: 100%; width: 100%;`, 33 | // we can set its parent element's width and height to cause container 34 | // queries to be evaluated. 35 | (parentElement as HTMLElement).style.height = `${height}px`; 36 | (parentElement as HTMLElement).style.width = `${width}px`; 37 | 38 | await timeout(); 39 | } 40 | -------------------------------------------------------------------------------- /docs-app/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ember Container Query Tests 10 | 11 | {{content-for "head"}} 12 | {{content-for "test-head"}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{content-for "head-footer"}} 20 | {{content-for "test-head-footer"}} 21 | 22 | 23 | {{content-for "body"}} 24 | {{content-for "test-body"}} 25 | 26 |

27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {{content-for "body-footer"}} 40 | {{content-for "test-body-footer"}} 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs-app/tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/tests/integration/.gitkeep -------------------------------------------------------------------------------- /docs-app/tests/integration/components/navigation-menu-test.gts: -------------------------------------------------------------------------------- 1 | import { array, hash } from '@ember/helper'; 2 | import { findAll, render } from '@ember/test-helpers'; 3 | import NavigationMenu from 'docs-app/components/navigation-menu'; 4 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 5 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 6 | import { module, test } from 'qunit'; 7 | 8 | module('Integration | Component | navigation-menu', function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render( 13 | , 19 | ); 20 | 21 | assert 22 | .dom('[data-test-nav="Main Navigation"]') 23 | .hasAria( 24 | 'label', 25 | 'Main Navigation', 26 | 'We can pass @name to specify the navigation.', 27 | ) 28 | .hasTagName('nav', 'We see the correct tag name.'); 29 | 30 | const links = findAll('[data-test-link]'); 31 | 32 | assert.strictEqual(links.length, 1, 'We see 1 link.'); 33 | 34 | assert 35 | .dom(links[0]) 36 | .hasAttribute('href', '/', 'We see the correct href for the 1st link.') 37 | .hasText('Home', 'We see the correct label for the 1st link.'); 38 | 39 | await a11yAudit(); 40 | 41 | assert.ok(true, 'We passed the accessibility audit.'); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/products/product/card-test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | render, 3 | type TestContext as BaseTestContext, 4 | } from '@ember/test-helpers'; 5 | import type { Product } from 'docs-app/data'; 6 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 7 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 8 | import { hbs } from 'ember-cli-htmlbars'; 9 | import { module, test } from 'qunit'; 10 | 11 | interface TestContext extends BaseTestContext { 12 | product: Product; 13 | } 14 | 15 | module('Integration | Component | products/product/card', function (hooks) { 16 | setupRenderingTest(hooks); 17 | 18 | hooks.beforeEach(function (this: TestContext) { 19 | this.product = { 20 | categoryId: 'cake', 21 | description: 'Made with organic herbs', 22 | id: '1', 23 | imageUrl: '', 24 | name: 'Vanilla Ice Cream Cake', 25 | price: 40, 26 | rating: 4.5, 27 | seller: "Amy's", 28 | shortDescription: 'Made with organic herbs', 29 | }; 30 | }); 31 | 32 | test('it renders', async function (this: TestContext, assert) { 33 | await render(hbs` 34 | 38 | `); 39 | 40 | assert 41 | .dom('[data-test-field="Name"]') 42 | .hasText('Vanilla Ice Cream Cake', 'We see the product name.'); 43 | 44 | assert 45 | .dom('[data-test-field="Short Description"]') 46 | .hasText( 47 | 'Made with organic herbs', 48 | 'We see the product short description.', 49 | ); 50 | 51 | assert 52 | .dom('[data-test-field="Price"]') 53 | .hasText('$40', 'We see the product price.'); 54 | 55 | assert 56 | .dom('[data-test-link="Learn More"]') 57 | .hasTagName('a', 'We see the correct tag name.') 58 | .hasText('Learn more', 'We see the learn more link.'); 59 | 60 | await a11yAudit(); 61 | 62 | assert.ok(true, 'We passed the accessibility audit.'); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/products/product/image-test.ts: -------------------------------------------------------------------------------- 1 | import { render, type TestContext } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | products/product/image', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders a placeholder in test environment', async function (this: TestContext, assert) { 10 | await render(hbs` 11 | 14 | `); 15 | 16 | assert.dom('img').doesNotExist('We should not make a network request.'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/ui/form/field-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { hbs } from 'ember-cli-htmlbars'; 5 | import { module, test } from 'qunit'; 6 | 7 | module('Integration | Component | ui/form/field', function (hooks) { 8 | setupRenderingTest(hooks); 9 | 10 | test('The component handles the field layout', async function (assert) { 11 | await render(hbs` 12 | 13 | <:label as |l|> 14 | 17 | 18 | 19 | <:field as |f|> 20 | 21 | 22 | 23 | `); 24 | 25 | assert.dom('[data-test-label]').hasText('Name', 'We see the label.'); 26 | 27 | assert.dom('[data-test-field="Name"]').hasValue('', 'We see the field.'); 28 | 29 | assert 30 | .dom('[data-test-feedback]') 31 | .doesNotExist('We should not see an error message.'); 32 | 33 | await a11yAudit(); 34 | 35 | assert.ok(true, 'We passed the accessibility audit.'); 36 | }); 37 | 38 | test('We can pass @errorMessage to show an error message', async function (assert) { 39 | await render(hbs` 40 | 41 | <:label as |l|> 42 | 45 | 46 | 47 | <:field as |f|> 48 | 49 | 50 | 51 | `); 52 | 53 | assert 54 | .dom('[data-test-feedback]') 55 | .hasText('Please provide a value.', 'We see the error message.'); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/ui/form/information-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | ui/form/information', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders nothing when we do not pass @title or @instructions', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | assert 15 | .dom('[data-test-title]') 16 | .doesNotExist('We should not see the form title.'); 17 | 18 | assert 19 | .dom('[data-test-instructions]') 20 | .doesNotExist('We should not see the form instructions.'); 21 | }); 22 | 23 | test('We can pass @title to display the form title', async function (assert) { 24 | await render(hbs` 25 | 26 | `); 27 | 28 | assert 29 | .dom('[data-test-title]') 30 | .hasAttribute('id', 'ember123-title', 'We see the correct ID.') 31 | .hasText('Contact me', 'We see the form title.'); 32 | 33 | assert 34 | .dom('[data-test-instructions]') 35 | .doesNotExist('We should not see the form instructions.'); 36 | }); 37 | 38 | test('We can pass @instructions to display the form instructions', async function (assert) { 39 | await render(hbs` 40 | 44 | `); 45 | 46 | assert 47 | .dom('[data-test-title]') 48 | .doesNotExist('We should not see the form title.'); 49 | 50 | assert 51 | .dom('[data-test-instructions]') 52 | .hasAttribute('id', 'ember123-instructions', 'We see the correct ID.') 53 | .hasText( 54 | 'Still have questions about ember-container-query? Try sending me a message.', 55 | 'We see the form instructions.', 56 | ); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/ui/page-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 4 | import { hbs } from 'ember-cli-htmlbars'; 5 | import { module, test } from 'qunit'; 6 | 7 | module('Integration | Component | ui/page', function (hooks) { 8 | setupRenderingTest(hooks); 9 | 10 | test('it renders', async function (assert) { 11 | await render(hbs` 12 | 13 |
14 | Content goes here. 15 |
16 |
17 | `); 18 | 19 | assert.dom('h1').hasText('Form', 'We see the title.'); 20 | 21 | assert.dom('[data-test-content]').exists('We see the yielded content.'); 22 | 23 | await a11yAudit(); 24 | 25 | assert.ok(true, 'We passed the accessibility audit.'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/widgets/widget-1-test.ts: -------------------------------------------------------------------------------- 1 | import { findAll, render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | widgets/widget-1', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | const titles = findAll('[data-test-title]'); 15 | 16 | assert.strictEqual(titles.length, 3, 'We see 3 titles.'); 17 | 18 | assert 19 | .dom(titles[0]) 20 | .hasText('Item 1', 'We see the correct text for the 1st title.'); 21 | 22 | assert 23 | .dom(titles[1]) 24 | .hasText('Item 2', 'We see the correct text for the 2nd title.'); 25 | 26 | assert 27 | .dom(titles[2]) 28 | .hasText('Item 3', 'We see the correct text for the 3rd title.'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/widgets/widget-2-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | widgets/widget-2', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | assert.ok(true); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/widgets/widget-3-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | widgets/widget-3', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | assert.dom('[data-test-tour-schedule]').exists('We see the tour schedule.'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/widgets/widget-4-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | widgets/widget-4', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | assert 15 | .dom('[data-test-link="All memos"]') 16 | .exists('We see the All memos link.'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /docs-app/tests/integration/components/widgets/widget-5-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Component | widgets/widget-5', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('The component renders', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | assert 15 | .dom('[data-test-call-to-action]') 16 | .hasText( 17 | 'What will you create with ember-container-query ?', 18 | 'We see the correct text for call-to-action.', 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /docs-app/tests/integration/modifiers/draw-stacked-chart-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Modifier | draw-stacked-chart', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('We can draw a chart', async function (assert) { 10 | await render(hbs` 11 |
12 | 13 |
14 | `); 15 | 16 | assert.ok(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /docs-app/tests/integration/modifiers/dynamic-css-grid-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { setupRenderingTest } from 'docs-app/tests/helpers'; 3 | import { hbs } from 'ember-cli-htmlbars'; 4 | import { module, test } from 'qunit'; 5 | 6 | module('Integration | Modifier | dynamic-css-grid', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('We can dynamically style the CSS grid', async function (assert) { 10 | await render(hbs` 11 |
12 |
13 | `); 14 | 15 | assert.ok(true); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /docs-app/tests/test-helper.ts: -------------------------------------------------------------------------------- 1 | import { setApplication } from '@ember/test-helpers'; 2 | import Application from 'docs-app/app'; 3 | import config from 'docs-app/config/environment'; 4 | import { setRunOptions } from 'ember-a11y-testing/test-support'; 5 | import { setupEmberOnerrorValidation, start } from 'ember-qunit'; 6 | import { loadTests } from 'ember-qunit/test-loader'; 7 | import QUnit from 'qunit'; 8 | import { setup } from 'qunit-dom'; 9 | 10 | setApplication(Application.create(config.APP)); 11 | 12 | setRunOptions({ 13 | rules: { 14 | 'scrollable-region-focusable': { 15 | enabled: false, 16 | }, 17 | }, 18 | }); 19 | 20 | setup(QUnit.assert); 21 | setupEmberOnerrorValidation(); 22 | loadTests(); 23 | start(); 24 | -------------------------------------------------------------------------------- /docs-app/tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/docs-app/tests/unit/.gitkeep -------------------------------------------------------------------------------- /docs-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "docs-app/tests/*": ["tests/*"], 7 | "docs-app/*": ["app/*"], 8 | "*": ["types/*"] 9 | }, 10 | "types": ["ember-source/types"] 11 | }, 12 | "include": ["app/**/*", "tests/**/*", "types/**/*"], 13 | "glint": { 14 | "environment": ["ember-loose", "ember-template-imports"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs-app/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import '@glint/environment-ember-loose'; 2 | import '@glint/environment-ember-template-imports'; 3 | 4 | import type { ComponentLike } from '@glint/template'; 5 | import type EmberContainerQueryRegistry from 'ember-container-query/template-registry'; 6 | import type EmberPageTitleRegistry from 'ember-page-title/template-registry'; 7 | import type EmberSvgJarRegistry from 'ember-svg-jar/template-registry'; 8 | import type EmberTruthHelpersRegistry from 'ember-truth-helpers/template-registry'; 9 | import type EmbroiderCssModulesRegistry from 'embroider-css-modules/template-registry'; 10 | 11 | type NavigationNarratorComponent = ComponentLike<{ 12 | Args: { 13 | skipTo: string; 14 | }; 15 | }>; 16 | 17 | declare module '@glint/environment-ember-loose/registry' { 18 | export default interface Registry 19 | extends EmberContainerQueryRegistry, 20 | EmberPageTitleRegistry, 21 | EmberSvgJarRegistry, 22 | EmberTruthHelpersRegistry, 23 | EmbroiderCssModulesRegistry { 24 | // Add any registry entries from other addons here that your addon itself uses (in non-strict mode templates) 25 | // See https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons 26 | NavigationNarrator: NavigationNarratorComponent; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace-root", 3 | "version": "6.0.1", 4 | "private": true, 5 | "description": "Workspace root for ember-container-query", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ijlee2/ember-container-query.git" 9 | }, 10 | "license": "MIT", 11 | "author": "Isaac J. Lee", 12 | "type": "module", 13 | "scripts": { 14 | "build": "concurrently \"pnpm:build:*\"", 15 | "build:addon": "pnpm --filter ember-container-query build", 16 | "build:docs-app": "pnpm --filter docs-app build", 17 | "format": "pnpm --filter \"*\" format", 18 | "lint": "pnpm --filter \"*\" lint && pnpm lint:package-json", 19 | "lint:fix": "pnpm --filter \"*\" lint:fix && pnpm lint:package-json:fix", 20 | "lint:package-json": "pnpm lint:package-json:fix --check", 21 | "lint:package-json:fix": "sort-package-json \"**/package.json\" --ignore \"**/{dist,node_modules}/**\"", 22 | "prepare": "pnpm build:addon", 23 | "release:prepare": "changeset version; update-workspace-root-version", 24 | "release:publish": "pnpm build && changeset publish", 25 | "start": "concurrently \"pnpm:start:*\" --restart-after 5000 --prefix-colors cyan,white,yellow", 26 | "start:addon": "pnpm --filter ember-container-query start", 27 | "start:docs-app": "pnpm --filter docs-app start", 28 | "start:test-app": "pnpm --filter test-app start", 29 | "test": "pnpm --filter \"*\" test" 30 | }, 31 | "devDependencies": { 32 | "@changesets/cli": "^2.29.4", 33 | "@changesets/get-github-info": "^0.6.0", 34 | "concurrently": "^9.1.2", 35 | "sort-package-json": "^3.2.1", 36 | "update-workspace-root-version": "^2.0.1" 37 | }, 38 | "packageManager": "pnpm@9.15.9", 39 | "engines": { 40 | "node": "20.* || >= 22", 41 | "pnpm": ">= 9" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/ember-container-query/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /declarations/ 3 | /dist/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.eslintcache 10 | 11 | # npm/pnpm/yarn pack output 12 | *.tgz 13 | -------------------------------------------------------------------------------- /packages/ember-container-query/.prettierignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /declarations/ 3 | /dist/ 4 | 5 | # misc 6 | !.* 7 | .*/ 8 | README.md 9 | 10 | # specific to this package 11 | -------------------------------------------------------------------------------- /packages/ember-container-query/.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('@ijlee2-frontend-configs/ember-template-lint'); 4 | -------------------------------------------------------------------------------- /packages/ember-container-query/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Isaac J. Lee 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 | -------------------------------------------------------------------------------- /packages/ember-container-query/addon-main.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { addonV1Shim } = require('@embroider/addon-shim'); 4 | 5 | module.exports = addonV1Shim(__dirname); 6 | -------------------------------------------------------------------------------- /packages/ember-container-query/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@babel/plugin-transform-typescript", 5 | { 6 | "allExtensions": true, 7 | "allowDeclareFields": true, 8 | "onlyRemoveTypeImports": true 9 | } 10 | ], 11 | "@embroider/addon-dev/template-colocation-plugin", 12 | [ 13 | "babel-plugin-ember-template-compilation", 14 | { 15 | "targetFormat": "hbs", 16 | "transforms": [] 17 | } 18 | ], 19 | [ 20 | "module:decorator-transforms", 21 | { 22 | "runtime": { 23 | "import": "decorator-transforms/runtime" 24 | } 25 | } 26 | ] 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/ember-container-query/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfiguration from '@ijlee2-frontend-configs/eslint-config-ember/v2-addon'; 2 | 3 | export default [ 4 | ...baseConfiguration, 5 | { 6 | files: ['**/*.{gts,ts}'], 7 | rules: { 8 | '@typescript-eslint/no-unsafe-call': 'off', 9 | '@typescript-eslint/no-unsafe-member-access': 'off', 10 | '@typescript-eslint/unbound-method': 'off', 11 | }, 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /packages/ember-container-query/prettier.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@ijlee2-frontend-configs/prettier/ember'; 2 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/components/container-query.css: -------------------------------------------------------------------------------- 1 | .container-query { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/components/container-query.gts: -------------------------------------------------------------------------------- 1 | import './container-query.css'; 2 | 3 | import { hash } from '@ember/helper'; 4 | import Component from '@glimmer/component'; 5 | import { tracked } from '@glimmer/tracking'; 6 | import { element } from 'ember-element-helper'; 7 | 8 | import type { 9 | Dimensions, 10 | Features, 11 | IndexSignatureParameter, 12 | QueryResults, 13 | } from '../modifiers/container-query.ts'; 14 | import { default as containerQuery } from '../modifiers/container-query.ts'; 15 | 16 | interface ContainerQuerySignature { 17 | Args: { 18 | dataAttributePrefix?: string; 19 | debounce?: number; 20 | features?: Features; 21 | tagName?: string; 22 | }; 23 | Blocks: { 24 | default: [ 25 | { 26 | dimensions?: Dimensions; 27 | features?: QueryResults; 28 | }, 29 | ]; 30 | }; 31 | Element: Element; 32 | } 33 | 34 | export default class ContainerQueryComponent< 35 | T extends IndexSignatureParameter, 36 | > extends Component> { 37 | @tracked dimensions?: Dimensions; 38 | @tracked queryResults?: QueryResults; 39 | 40 | // The dynamic tag is restricted to be immutable 41 | tagName = this.args.tagName ?? 'div'; 42 | 43 | updateState = ({ 44 | dimensions, 45 | queryResults, 46 | }: { 47 | dimensions: Dimensions; 48 | queryResults: QueryResults; 49 | }): void => { 50 | this.dimensions = dimensions; 51 | this.queryResults = queryResults; 52 | }; 53 | 54 | 70 | } 71 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/helpers/aspect-ratio.ts: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | 3 | import type { Metadata } from '../modifiers/container-query.ts'; 4 | 5 | interface AspectRatioHelperSignature { 6 | Args: { 7 | Named: { 8 | max?: number; 9 | min?: number; 10 | }; 11 | Positional: []; 12 | }; 13 | Return: Metadata; 14 | } 15 | 16 | const AspectRatioHelper = helper( 17 | (_positional, named) => { 18 | const dimension = 'aspectRatio'; 19 | const max = named.max ?? Infinity; 20 | const min = named.min ?? 0; 21 | 22 | return { dimension, max, min }; 23 | }, 24 | ); 25 | 26 | export default AspectRatioHelper; 27 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/helpers/height.ts: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | 3 | import type { Metadata } from '../modifiers/container-query.ts'; 4 | 5 | interface HeightHelperSignature { 6 | Args: { 7 | Named: { 8 | max?: number; 9 | min?: number; 10 | }; 11 | Positional: []; 12 | }; 13 | Return: Metadata; 14 | } 15 | 16 | const HeightHelper = helper((_positional, named) => { 17 | const dimension = 'height'; 18 | const max = named.max ?? Infinity; 19 | const min = named.min ?? 0; 20 | 21 | return { dimension, max, min }; 22 | }); 23 | 24 | export default HeightHelper; 25 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/helpers/width.ts: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | 3 | import type { Metadata } from '../modifiers/container-query.ts'; 4 | 5 | interface WidthHelperSignature { 6 | Args: { 7 | Named: { 8 | max?: number; 9 | min?: number; 10 | }; 11 | Positional: []; 12 | }; 13 | Return: Metadata; 14 | } 15 | 16 | const WidthHelper = helper((_positional, named) => { 17 | const dimension = 'width'; 18 | const max = named.max ?? Infinity; 19 | const min = named.min ?? 0; 20 | 21 | return { dimension, max, min }; 22 | }); 23 | 24 | export default WidthHelper; 25 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ContainerQuery } from './components/container-query.gts'; 2 | export { default as aspectRatio } from './helpers/aspect-ratio.ts'; 3 | export { default as height } from './helpers/height.ts'; 4 | export { default as width } from './helpers/width.ts'; 5 | export type { 6 | Dimensions, 7 | Features, 8 | IndexSignatureParameter, 9 | Metadata, 10 | QueryResults, 11 | } from './modifiers/container-query.ts'; 12 | export { default as containerQuery } from './modifiers/container-query.ts'; 13 | -------------------------------------------------------------------------------- /packages/ember-container-query/src/template-registry.ts: -------------------------------------------------------------------------------- 1 | import type ContainerQueryComponent from './components/container-query.gts'; 2 | import type AspectRatioHelper from './helpers/aspect-ratio.ts'; 3 | import type HeightHelper from './helpers/height.ts'; 4 | import type WidthHelper from './helpers/width.ts'; 5 | import type ContainerQueryModifier from './modifiers/container-query.ts'; 6 | 7 | export default interface EmberContainerQueryRegistry { 8 | ContainerQuery: typeof ContainerQueryComponent; 9 | 'aspect-ratio': typeof AspectRatioHelper; 10 | 'container-query': typeof ContainerQueryModifier; 11 | height: typeof HeightHelper; 12 | width: typeof WidthHelper; 13 | } 14 | -------------------------------------------------------------------------------- /packages/ember-container-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "compilerOptions": { 4 | "allowImportingTsExtensions": true, 5 | "allowJs": true, 6 | "declarationDir": "declarations", 7 | "rootDir": "./src", 8 | "types": ["ember-source/types"] 9 | }, 10 | "include": ["src/**/*", "unpublished-development-types/**/*"], 11 | "glint": { 12 | "environment": ["ember-loose", "ember-template-imports"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/ember-container-query/unpublished-development-types/index.d.ts: -------------------------------------------------------------------------------- 1 | // Add any types here that you need for local development only. 2 | // These will *not* be published as part of your addon, so be careful that your published code does not rely on them! 3 | 4 | import '@glint/environment-ember-loose'; 5 | import '@glint/environment-ember-template-imports'; 6 | 7 | import type EmberElementHelperRegistry from 'ember-element-helper/template-registry'; 8 | 9 | import type EmberContainerQueryRegistry from '../src/template-registry.ts'; 10 | 11 | declare module '@glint/environment-ember-loose/registry' { 12 | export default interface Registry 13 | extends EmberContainerQueryRegistry, 14 | EmberElementHelperRegistry { 15 | // Add any registry entries from other addons here that your addon itself uses (in non-strict mode templates) 16 | // See https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - docs-app 3 | - packages/ember-container-query 4 | - test-app 5 | -------------------------------------------------------------------------------- /test-app/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript 4 | rather than JavaScript by default, when a TypeScript version of a given blueprint is available. 5 | */ 6 | "isTypeScriptProject": true 7 | } 8 | -------------------------------------------------------------------------------- /test-app/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /declarations/ 3 | /dist/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.env* 10 | /.pnp* 11 | /.eslintcache 12 | /.pnpm-debug.log 13 | /.stylelintcache 14 | /coverage/ 15 | /testem.log 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /npm-shrinkwrap.json.ember-try 20 | /package.json.ember-try 21 | /package-lock.json.ember-try 22 | /yarn.lock.ember-try 23 | 24 | # broccoli-debug 25 | /DEBUG/ 26 | -------------------------------------------------------------------------------- /test-app/.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # misc 8 | /coverage/ 9 | !.* 10 | .*/ 11 | *.html 12 | 13 | # specific to this package 14 | -------------------------------------------------------------------------------- /test-app/.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('@ijlee2-frontend-configs/ember-template-lint'); 4 | -------------------------------------------------------------------------------- /test-app/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["dist"] 3 | } 4 | -------------------------------------------------------------------------------- /test-app/app/app.ts: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import loadInitializers from 'ember-load-initializers'; 3 | import Resolver from 'ember-resolver'; 4 | 5 | import config from './config/environment'; 6 | 7 | export default class App extends Application { 8 | modulePrefix = config.modulePrefix; 9 | podModulePrefix = config.podModulePrefix; 10 | Resolver = Resolver; 11 | } 12 | 13 | loadInitializers(App, config.modulePrefix); 14 | -------------------------------------------------------------------------------- /test-app/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/test-app/app/components/.gitkeep -------------------------------------------------------------------------------- /test-app/app/config/environment.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Type declarations for 3 | * import config from 'my-app/config/environment' 4 | */ 5 | declare const config: { 6 | APP: Record; 7 | environment: string; 8 | locationType: 'history' | 'hash' | 'none' | 'auto'; 9 | modulePrefix: string; 10 | podModulePrefix: string; 11 | rootURL: string; 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /test-app/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/test-app/app/controllers/.gitkeep -------------------------------------------------------------------------------- /test-app/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/test-app/app/helpers/.gitkeep -------------------------------------------------------------------------------- /test-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ember Container Query 10 | 11 | {{content-for "head"}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{content-for "head-footer"}} 20 | 21 | 22 | {{content-for "body"}} 23 | 24 | 25 | 26 | 27 | {{content-for "body-footer"}} 28 | 29 | 30 | -------------------------------------------------------------------------------- /test-app/app/router.ts: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | 3 | import config from './config/environment'; 4 | 5 | export default class Router extends EmberRouter { 6 | location = config.locationType; 7 | rootURL = config.rootURL; 8 | } 9 | 10 | Router.map(function () { 11 | // Add routes here 12 | }); 13 | -------------------------------------------------------------------------------- /test-app/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/test-app/app/routes/.gitkeep -------------------------------------------------------------------------------- /test-app/app/styles/app.css: -------------------------------------------------------------------------------- 1 | /* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */ 2 | -------------------------------------------------------------------------------- /test-app/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Ember Container Query"}} 2 | 3 |
4 |
5 |
6 | {{outlet}} 7 |
8 |
9 | 10 | 23 |
-------------------------------------------------------------------------------- /test-app/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 |
2 |

Welcome!

3 | 4 | 5 |

6 | This is the 7 | test-app 8 | for 9 | ember-container-query. Did you want to run 10 | the 11 | docs-app 12 | instead? 13 |

14 |
15 |
-------------------------------------------------------------------------------- /test-app/config/dependency-lint.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | allowedVersions: {}, 3 | }; 4 | -------------------------------------------------------------------------------- /test-app/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "6.4.0", 7 | "blueprints": [ 8 | { 9 | "name": "app", 10 | "outputRepo": "https://github.com/ember-cli/ember-new-output", 11 | "codemodsSource": "ember-app-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": ["--embroider", "--typescript"] 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test-app/config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { embroiderOptimized, embroiderSafe } = require('@embroider/test-setup'); 4 | 5 | module.exports = async function () { 6 | const { default: latestVersion } = await import('latest-version'); 7 | 8 | return { 9 | packageManager: 'pnpm', 10 | scenarios: [ 11 | { 12 | name: 'ember-lts-4.12', 13 | npm: { 14 | devDependencies: { 15 | 'ember-source': '~4.12.0', 16 | }, 17 | }, 18 | }, 19 | { 20 | name: 'ember-lts-5.12', 21 | npm: { 22 | devDependencies: { 23 | 'ember-source': '~5.12.0', 24 | }, 25 | }, 26 | }, 27 | { 28 | name: 'ember-release', 29 | npm: { 30 | devDependencies: { 31 | 'ember-source': await latestVersion('ember-source'), 32 | }, 33 | }, 34 | }, 35 | { 36 | name: 'ember-beta', 37 | npm: { 38 | devDependencies: { 39 | 'ember-source': await latestVersion('ember-source', { 40 | version: 'beta', 41 | }), 42 | }, 43 | }, 44 | }, 45 | { 46 | name: 'ember-canary', 47 | npm: { 48 | devDependencies: { 49 | 'ember-source': await latestVersion('ember-source', { 50 | version: 'alpha', 51 | }), 52 | }, 53 | }, 54 | }, 55 | embroiderSafe(), 56 | embroiderOptimized(), 57 | ], 58 | }; 59 | }; 60 | -------------------------------------------------------------------------------- /test-app/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'test-app', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'history', 9 | EmberENV: { 10 | EXTEND_PROTOTYPES: false, 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 14 | }, 15 | }, 16 | 17 | APP: { 18 | // Here you can pass flags/options to your application instance 19 | // when it is created 20 | }, 21 | }; 22 | 23 | if (environment === 'development') { 24 | // ENV.APP.LOG_RESOLVER = true; 25 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 26 | // ENV.APP.LOG_TRANSITIONS = true; 27 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 28 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 29 | } 30 | 31 | if (environment === 'test') { 32 | // Testem prefers this... 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | ENV.APP.autoboot = false; 41 | } 42 | 43 | if (environment === 'production') { 44 | // here you can enable a production-specific feature 45 | } 46 | 47 | return ENV; 48 | }; 49 | -------------------------------------------------------------------------------- /test-app/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "no-implicit-route-model": true, 6 | "template-only-glimmer-components": true 7 | } 8 | -------------------------------------------------------------------------------- /test-app/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | browsers: [ 5 | 'last 2 Chrome versions', 6 | 'last 2 Edge versions', 7 | 'last 2 Firefox versions', 8 | 'last 2 Safari versions', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /test-app/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sideWatch = require('@embroider/broccoli-side-watch'); 4 | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 5 | 6 | module.exports = function (defaults) { 7 | const app = new EmberApp(defaults, { 8 | // Add options here 9 | autoImport: { 10 | watchDependencies: ['ember-container-query'], 11 | }, 12 | 13 | 'ember-cli-babel': { 14 | enableTypeScriptTransform: true, 15 | }, 16 | 17 | trees: { 18 | app: sideWatch('app', { 19 | watching: ['../packages/ember-container-query/src'], 20 | }), 21 | }, 22 | }); 23 | 24 | const { maybeEmbroider } = require('@embroider/test-setup'); 25 | 26 | return maybeEmbroider(app); 27 | }; 28 | -------------------------------------------------------------------------------- /test-app/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@ijlee2-frontend-configs/eslint-config-ember/v1-app'; 2 | -------------------------------------------------------------------------------- /test-app/prettier.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@ijlee2-frontend-configs/prettier/ember'; 2 | -------------------------------------------------------------------------------- /test-app/public/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/test-app/public/assets/favicon.png -------------------------------------------------------------------------------- /test-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /test-app/testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | browser_args: { 5 | Chrome: { 6 | ci: [ 7 | // --no-sandbox is needed when running Chrome inside a container 8 | process.env.CI ? '--no-sandbox' : null, 9 | '--headless', 10 | '--disable-dev-shm-usage', 11 | '--disable-software-rasterizer', 12 | '--mute-audio', 13 | '--remote-debugging-port=0', 14 | '--window-size=1440,900', 15 | ].filter(Boolean), 16 | }, 17 | }, 18 | browser_start_timeout: 120, 19 | disable_watching: true, 20 | launch_in_ci: ['Chrome'], 21 | launch_in_dev: ['Chrome'], 22 | test_page: 'tests/index.html?hidepassed&nolint', 23 | }; 24 | -------------------------------------------------------------------------------- /test-app/tests/acceptance/index/accessibility-test.ts: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 3 | import { module, test } from 'qunit'; 4 | import { setupApplicationTest, timeout } from 'test-app/tests/helpers'; 5 | 6 | module('Acceptance | index', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | test('Accessibility audit', async function (assert) { 10 | await visit('/'); 11 | await timeout(); 12 | 13 | await a11yAudit(); 14 | 15 | assert.ok(true, 'We passed Axe tests.'); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test-app/tests/helpers/container-query.ts: -------------------------------------------------------------------------------- 1 | export * from './container-query/assert-data-attributes'; 2 | export * from './container-query/assert-dimensions'; 3 | export * from './container-query/assert-features'; 4 | -------------------------------------------------------------------------------- /test-app/tests/helpers/container-query/assert-data-attributes.ts: -------------------------------------------------------------------------------- 1 | import { find } from '@ember/test-helpers'; 2 | 3 | type DataAttributes = Record; 4 | 5 | export function assertDataAttributes( 6 | assert: Assert, 7 | dataAttributes: DataAttributes = {}, 8 | ): void { 9 | const containerQuery = find('[data-test-container-query]'); 10 | 11 | for (const [dataAttributeName, expectedValue] of Object.entries( 12 | dataAttributes, 13 | )) { 14 | switch (expectedValue) { 15 | case undefined: { 16 | assert 17 | .dom(containerQuery) 18 | .doesNotHaveAttribute( 19 | dataAttributeName, 20 | `The container doesn't have the attribute "${dataAttributeName}".`, 21 | ); 22 | 23 | break; 24 | } 25 | 26 | default: { 27 | assert 28 | .dom(containerQuery) 29 | .hasAttribute( 30 | dataAttributeName, 31 | expectedValue, 32 | `The container has the attribute "${dataAttributeName}".`, 33 | ); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test-app/tests/helpers/container-query/assert-dimensions.ts: -------------------------------------------------------------------------------- 1 | import { find } from '@ember/test-helpers'; 2 | 3 | type Dimensions = { 4 | height: number; 5 | width: number; 6 | }; 7 | 8 | export function assertDimensions(assert: Assert, dimensions: Dimensions): void { 9 | const { height: expectedHeight, width: expectedWidth } = dimensions; 10 | 11 | // Check width and height 12 | assert 13 | .dom('[data-test-width-height]') 14 | .hasText( 15 | `${expectedWidth} x ${expectedHeight}`, 16 | 'Width and height are correct.', 17 | ); 18 | 19 | // Check aspect ratio 20 | const targetElement = find('[data-test-aspect-ratio]') as HTMLElement; 21 | 22 | const aspectRatio = parseFloat(targetElement.textContent!.trim()); 23 | const expectedAspectRatio = expectedWidth / expectedHeight; 24 | const tolerance = 0.001; 25 | 26 | if (expectedAspectRatio === Infinity) { 27 | assert.true( 28 | aspectRatio === expectedAspectRatio, 29 | 'Aspect ratio is correct.', 30 | ); 31 | 32 | return; 33 | } 34 | 35 | const relativeError = 36 | Math.abs(aspectRatio - expectedAspectRatio) / Math.abs(expectedAspectRatio); 37 | 38 | assert.true(relativeError < tolerance, 'Aspect ratio is correct.'); 39 | } 40 | -------------------------------------------------------------------------------- /test-app/tests/helpers/container-query/assert-features.ts: -------------------------------------------------------------------------------- 1 | type Features = Record; 2 | 3 | export function assertFeatures(assert: Assert, features: Features = {}): void { 4 | for (const [featureName, meetsFeature] of Object.entries(features)) { 5 | switch (meetsFeature) { 6 | case true: { 7 | assert 8 | .dom(`[data-test-feature="${featureName}"]`) 9 | .hasText('true', `The container meets the feature "${featureName}".`); 10 | 11 | break; 12 | } 13 | 14 | case false: { 15 | assert 16 | .dom(`[data-test-feature="${featureName}"]`) 17 | .hasText( 18 | 'false', 19 | `The container doesn't meet the feature "${featureName}".`, 20 | ); 21 | 22 | break; 23 | } 24 | 25 | case undefined: { 26 | assert 27 | .dom(`[data-test-feature="${featureName}"]`) 28 | .hasNoText( 29 | `The container doesn't meet the feature "${featureName}".`, 30 | ); 31 | 32 | break; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test-app/tests/helpers/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | setupApplicationTest as upstreamSetupApplicationTest, 3 | setupRenderingTest as upstreamSetupRenderingTest, 4 | setupTest as upstreamSetupTest, 5 | type SetupTestOptions, 6 | } from 'ember-qunit'; 7 | 8 | // This file exists to provide wrappers around ember-qunit's 9 | // test setup functions. This way, you can easily extend the setup that is 10 | // needed per test type. 11 | 12 | function setupApplicationTest( 13 | hooks: NestedHooks, 14 | options?: SetupTestOptions, 15 | ): void { 16 | upstreamSetupApplicationTest(hooks, options); 17 | 18 | // Additional setup for application tests can be done here. 19 | // 20 | // For example, if you need an authenticated session for each 21 | // application test, you could do: 22 | // 23 | // hooks.beforeEach(async function () { 24 | // await authenticateSession(); // ember-simple-auth 25 | // }); 26 | // 27 | // This is also a good place to call test setup functions coming 28 | // from other addons: 29 | // 30 | // setupIntl(hooks, 'en-us'); // ember-intl 31 | // setupMirage(hooks); // ember-cli-mirage 32 | } 33 | 34 | function setupRenderingTest( 35 | hooks: NestedHooks, 36 | options?: SetupTestOptions, 37 | ): void { 38 | upstreamSetupRenderingTest(hooks, options); 39 | 40 | // Additional setup for rendering tests can be done here. 41 | } 42 | 43 | function setupTest(hooks: NestedHooks, options?: SetupTestOptions): void { 44 | upstreamSetupTest(hooks, options); 45 | 46 | // Additional setup for unit tests can be done here. 47 | } 48 | 49 | export { setupApplicationTest, setupRenderingTest, setupTest }; 50 | 51 | export * from './container-query'; 52 | export * from './resize-container'; 53 | -------------------------------------------------------------------------------- /test-app/tests/helpers/resize-container.ts: -------------------------------------------------------------------------------- 1 | import { assert } from '@ember/debug'; 2 | import { later } from '@ember/runloop'; 3 | import { find } from '@ember/test-helpers'; 4 | 5 | // This is a magic number. It is the time (in ms) for things to `settle` 6 | // after a resize. It is the time that we need to wait before assertions 7 | // that should pass will always pass. 8 | const RERENDER_TIME = 50; 9 | 10 | export function timeout(milliseconds = RERENDER_TIME): Promise { 11 | return new Promise((resolve) => { 12 | // @ts-expect-error: Did type definition for `later` change in ember-source@5.1? 13 | // eslint-disable-next-line ember/no-runloop 14 | later(resolve, milliseconds); 15 | }); 16 | } 17 | 18 | export async function resizeContainer({ 19 | height, 20 | width, 21 | }: { 22 | height: number; 23 | width: number; 24 | }): Promise { 25 | const parentElement = find('[data-test-parent-element]'); 26 | 27 | assert( 28 | 'Please create a parent element with the test selector `data-test-parent-element`.', 29 | parentElement, 30 | ); 31 | 32 | // Since has a style of `height: 100%; width: 100%;`, 33 | // we can set its parent element's width and height to cause container 34 | // queries to be evaluated. 35 | (parentElement as HTMLElement).style.height = `${height}px`; 36 | (parentElement as HTMLElement).style.width = `${width}px`; 37 | 38 | await timeout(); 39 | } 40 | -------------------------------------------------------------------------------- /test-app/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ember Container Query Tests 10 | 11 | {{content-for "head"}} 12 | {{content-for "test-head"}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{content-for "head-footer"}} 20 | {{content-for "test-head-footer"}} 21 | 22 | 23 | {{content-for "body"}} 24 | {{content-for "test-body"}} 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {{content-for "body-footer"}} 40 | {{content-for "test-body-footer"}} 41 | 42 | 43 | -------------------------------------------------------------------------------- /test-app/tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ijlee2/ember-container-query/9aafae9e19b3eae4730b3625383ad4522c864a6e/test-app/tests/integration/.gitkeep -------------------------------------------------------------------------------- /test-app/tests/integration/helpers/aspect-ratio-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { hbs } from 'ember-cli-htmlbars'; 3 | import { module, test } from 'qunit'; 4 | import { setupRenderingTest } from 'test-app/tests/helpers'; 5 | 6 | module('Integration | Helper | aspect-ratio', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('can return a hash with default values', async function (assert) { 10 | await render(hbs` 11 | {{#let (aspect-ratio) as |output|}} 12 |

{{output.dimension}}

13 |

{{output.min}}

14 |

{{output.max}}

15 | {{/let}} 16 | `); 17 | 18 | assert.dom('[data-test-value="dimension"]').hasText('aspectRatio'); 19 | assert.dom('[data-test-value="min"]').hasText('0'); 20 | assert.dom('[data-test-value="max"]').hasText('Infinity'); 21 | }); 22 | 23 | test('if min and max are provided, returns them as they are', async function (assert) { 24 | await render(hbs` 25 | {{#let (aspect-ratio max=0.75 min=0.25) as |output|}} 26 |

{{output.dimension}}

27 |

{{output.min}}

28 |

{{output.max}}

29 | {{/let}} 30 | `); 31 | 32 | assert.dom('[data-test-value="dimension"]').hasText('aspectRatio'); 33 | assert.dom('[data-test-value="min"]').hasText('0.25'); 34 | assert.dom('[data-test-value="max"]').hasText('0.75'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test-app/tests/integration/helpers/height-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { hbs } from 'ember-cli-htmlbars'; 3 | import { module, test } from 'qunit'; 4 | import { setupRenderingTest } from 'test-app/tests/helpers'; 5 | 6 | module('Integration | Helper | height', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('can return a hash with default values', async function (assert) { 10 | await render(hbs` 11 | {{#let (height) as |output|}} 12 |

{{output.dimension}}

13 |

{{output.min}}

14 |

{{output.max}}

15 | {{/let}} 16 | `); 17 | 18 | assert.dom('[data-test-value="dimension"]').hasText('height'); 19 | assert.dom('[data-test-value="min"]').hasText('0'); 20 | assert.dom('[data-test-value="max"]').hasText('Infinity'); 21 | }); 22 | 23 | test('if min and max are provided, returns them as they are', async function (assert) { 24 | await render(hbs` 25 | {{#let (height max=200 min=100) as |output|}} 26 |

{{output.dimension}}

27 |

{{output.min}}

28 |

{{output.max}}

29 | {{/let}} 30 | `); 31 | 32 | assert.dom('[data-test-value="dimension"]').hasText('height'); 33 | assert.dom('[data-test-value="min"]').hasText('100'); 34 | assert.dom('[data-test-value="max"]').hasText('200'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test-app/tests/integration/helpers/width-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { hbs } from 'ember-cli-htmlbars'; 3 | import { module, test } from 'qunit'; 4 | import { setupRenderingTest } from 'test-app/tests/helpers'; 5 | 6 | module('Integration | Helper | width', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('can return a hash with default values', async function (assert) { 10 | await render(hbs` 11 | {{#let (width) as |output|}} 12 |

{{output.dimension}}

13 |

{{output.min}}

14 |

{{output.max}}

15 | {{/let}} 16 | `); 17 | 18 | assert.dom('[data-test-value="dimension"]').hasText('width'); 19 | assert.dom('[data-test-value="min"]').hasText('0'); 20 | assert.dom('[data-test-value="max"]').hasText('Infinity'); 21 | }); 22 | 23 | test('if min and max are provided, returns them as they are', async function (assert) { 24 | await render(hbs` 25 | {{#let (width max=200 min=100) as |output|}} 26 |

{{output.dimension}}

27 |

{{output.min}}

28 |

{{output.max}}

29 | {{/let}} 30 | `); 31 | 32 | assert.dom('[data-test-value="dimension"]').hasText('width'); 33 | assert.dom('[data-test-value="min"]').hasText('100'); 34 | assert.dom('[data-test-value="max"]').hasText('200'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test-app/tests/integration/modifiers/container-query-test.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@ember/test-helpers'; 2 | import { hbs } from 'ember-cli-htmlbars'; 3 | import { module, test } from 'qunit'; 4 | import { setupRenderingTest } from 'test-app/tests/helpers'; 5 | 6 | module('Integration | Modifier | container-query', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('We can call the modifier without passing arguments', async function (assert) { 10 | await render(hbs` 11 | {{! template-lint-disable no-inline-styles }} 12 |
13 |
14 |
15 |
16 | `); 17 | 18 | assert.ok(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test-app/tests/test-helper.ts: -------------------------------------------------------------------------------- 1 | import { setApplication } from '@ember/test-helpers'; 2 | import { setupEmberOnerrorValidation, start } from 'ember-qunit'; 3 | import { loadTests } from 'ember-qunit/test-loader'; 4 | import QUnit from 'qunit'; 5 | import { setup } from 'qunit-dom'; 6 | import Application from 'test-app/app'; 7 | import config from 'test-app/config/environment'; 8 | 9 | setApplication(Application.create(config.APP)); 10 | 11 | setup(QUnit.assert); 12 | setupEmberOnerrorValidation(); 13 | loadTests(); 14 | start(); 15 | -------------------------------------------------------------------------------- /test-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "test-app/tests/*": ["tests/*"], 7 | "test-app/*": ["app/*"], 8 | "*": ["types/*"] 9 | }, 10 | "types": ["ember-source/types"] 11 | }, 12 | "include": ["app/**/*", "tests/**/*", "types/**/*"], 13 | "glint": { 14 | "environment": ["ember-loose", "ember-template-imports"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-app/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import '@glint/environment-ember-loose'; 2 | import '@glint/environment-ember-template-imports'; 3 | 4 | import type EmberContainerQueryRegistry from 'ember-container-query/template-registry'; 5 | import type EmberPageTitleRegistry from 'ember-page-title/template-registry'; 6 | 7 | declare module '@glint/environment-ember-loose/registry' { 8 | export default interface Registry 9 | extends EmberContainerQueryRegistry, 10 | EmberPageTitleRegistry { 11 | // Add any registry entries from other addons here that your addon itself uses (in non-strict mode templates) 12 | // See https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons 13 | } 14 | } 15 | --------------------------------------------------------------------------------