Skip to content

Commit ecdbafa

Browse files
Merge pull request #542 from universal-ember/nvp/12/tablist
New Component: Tabs
2 parents 99127c5 + 305bf6d commit ecdbafa

File tree

20 files changed

+2125
-414
lines changed

20 files changed

+2125
-414
lines changed

dev/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"devDependencies": {
2525
"@babel/core": "^7.28.5",
2626
"@nullvoxpopuli/eslint-configs": "^5.3.4",
27-
"eslint": "^9.38.0",
27+
"eslint": "^9.39.0",
2828
"prettier": "^3.2.5",
2929
"typescript": "^5.9.3"
3030
},

docs-app/.env.development

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# This file is committed to git and should not contain any secrets.
2+
#
3+
# Vite recommends using .env.local or .env.[mode].local if you need to manage secrets
4+
# SEE: https://vite.dev/guide/env-and-mode.html#env-files for more information.
5+
6+
7+
# Default NODE_ENV with vite build --mode=test is production
8+
NODE_ENV=development

docs-app/app/components/tabs.gts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/**
2+
Styled tabs for documentation
3+
*/
4+
5+
import {
6+
type ButtonType,
7+
type ContainerType,
8+
type ContentType,
9+
Tabs as PrimitiveTabs,
10+
} from 'ember-primitives/components/tabs';
11+
12+
import type { TOC } from '@ember/component/template-only';
13+
import type { ComponentLike, WithBoundArgs } from '@glint/template';
14+
15+
function isString(x: unknown): x is string {
16+
return typeof x === 'string';
17+
}
18+
19+
const StyledButton: TOC<{ Args: { button: ButtonType }; Blocks: { default: [] } }> = <template>
20+
<@button class="tab">
21+
{{yield}}
22+
</@button>
23+
24+
<style scoped>
25+
.tab {
26+
color: black;
27+
display: inline-block;
28+
padding: 0.25rem 0.5rem;
29+
background: hsl(220deg 20% 94%);
30+
outline: none;
31+
font-weight: bold;
32+
cursor: pointer;
33+
box-shadow: inset 0 0px 1px black;
34+
}
35+
.tab[aria-selected="true"] {
36+
background: white;
37+
box-shadow: inset 0 -4px 0px orange;
38+
}
39+
.tab:first-of-type {
40+
border-top-left-radius: 0.25rem;
41+
}
42+
.tab:last-of-type {
43+
border-top-right-radius: 0.25rem;
44+
}
45+
</style>
46+
</template>;
47+
48+
const StyledContent: TOC<{ Args: { content: ContentType }; Blocks: { default: [] } }> = <template>
49+
<@content class="tabpanel">
50+
{{yield}}
51+
</@content>
52+
53+
<style scoped>
54+
.tabpanel {
55+
color: black;
56+
overflow: auto;
57+
max-height: 20rem;
58+
width: 100%;
59+
max-width: 100%;
60+
border-radius: 0.75rem;
61+
background-image: linear-gradient(to right, var(--tw-gradient-stops));
62+
--tw-gradient-from: #7c3aed var(--tw-gradient-from-position);
63+
--tw-gradient-to: rgb(124 58 237 / 0) var(--tw-gradient-to-position);
64+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
65+
--tw-gradient-to: #4f46e5 var(--tw-gradient-to-position);
66+
padding: 5rem;
67+
--tw-text-opacity: 1;
68+
color: rgb(248 250 252 / var(--tw-text-opacity, 1));
69+
}
70+
</style>
71+
</template>;
72+
73+
const StyledTab: TOC<
74+
| {
75+
Args: {
76+
tab: ContainerType;
77+
label: never;
78+
content: never;
79+
};
80+
Blocks: {
81+
default: [button: ButtonType, content: ContentType];
82+
};
83+
}
84+
| {
85+
Args: {
86+
label: string | ComponentLike;
87+
content: string | ComponentLike;
88+
tab: ContainerType;
89+
};
90+
Blocks: {
91+
default: [];
92+
};
93+
}
94+
> = <template>
95+
<@tab as |UnstyledButton UnstyledContent|>
96+
{{#let
97+
(component StyledButton button=UnstyledButton)
98+
(component StyledContent content=UnstyledContent)
99+
as |Button Content|
100+
}}
101+
102+
{{#if @label}}
103+
<Button>
104+
{{#if (isString @label)}}
105+
{{@label}}
106+
{{else}}
107+
<@label />
108+
{{/if}}
109+
</Button>
110+
111+
<Content>
112+
{{#if @content}}
113+
{{#if (isString @content)}}
114+
{{@content}}
115+
{{else}}
116+
<@content />
117+
{{/if}}
118+
{{else}}
119+
{{yield}}
120+
{{/if}}
121+
</Content>
122+
{{else}}
123+
{{! @glint-expect-error The types here are too crazy so I ignore them and define the public API}}
124+
{{yield Button Content}}
125+
{{/if}}
126+
{{/let}}
127+
</@tab>
128+
</template>;
129+
130+
export const Tabs: TOC<{
131+
Blocks: {
132+
default: [WithBoundArgs<typeof StyledTab, 'tab'>];
133+
};
134+
}> = <template>
135+
<PrimitiveTabs class="docs-tabs" as |Tab|>
136+
{{yield (component StyledTab tab=Tab)}}
137+
</PrimitiveTabs>
138+
139+
<style scoped>
140+
.docs-tabs {
141+
padding: 1rem;
142+
143+
> [role="tablist"] {
144+
min-width: 100%;
145+
padding-left: 0.75rem;
146+
}
147+
}
148+
</style>
149+
</template>;

docs-app/app/routes/application.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
88

99
import { Callout } from '@universal-ember/docs-support';
1010

11+
import { Tabs } from '../components/tabs.gts';
1112
import { APIDocs, ComponentSignature, ModifierSignature } from './api-docs';
1213

1314
export default class Application extends Route {
@@ -38,6 +39,7 @@ export default class Application extends Route {
3839
APIDocs,
3940
ComponentSignature,
4041
ModifierSignature,
42+
Tabs,
4143
},
4244
resolve: {
4345
// ember-primitives
@@ -47,6 +49,7 @@ export default class Application extends Route {
4749
'ember-primitives/on-resize': import('ember-primitives/on-resize'),
4850
'ember-primitives/color-scheme': import('ember-primitives/color-scheme'),
4951
'ember-primitives/components/form': import('ember-primitives/components/form'),
52+
'ember-primitives/components/tabs': import('ember-primitives/components/tabs'),
5053
'ember-primitives/components/portal': import('ember-primitives/components/portal'),
5154
'ember-primitives/components/portal-targets': import(
5255
'ember-primitives/components/portal-targets'

docs-app/babel.config.cjs

Lines changed: 0 additions & 49 deletions
This file was deleted.

docs-app/babel.config.mjs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { fileURLToPath } from "node:url";
2+
import { scopedCSS } from "ember-scoped-css/babel";
3+
import { babelCompatSupport, templateCompatSupport } from "@embroider/compat/babel";
4+
5+
export default {
6+
plugins: [
7+
[
8+
"@babel/plugin-transform-typescript",
9+
{
10+
allExtensions: true,
11+
onlyRemoveTypeImports: true,
12+
allowDeclareFields: true,
13+
},
14+
],
15+
scopedCSS(),
16+
[
17+
"babel-plugin-ember-template-compilation",
18+
{
19+
compilerPath: "ember-source/dist/ember-template-compiler.js",
20+
enableLegacyModules: [
21+
"ember-cli-htmlbars",
22+
"ember-cli-htmlbars-inline-precompile",
23+
"htmlbars-inline-precompile",
24+
],
25+
transforms: [...templateCompatSupport(), scopedCSS.template({})],
26+
},
27+
],
28+
[
29+
"module:decorator-transforms",
30+
{
31+
runtime: {
32+
import: fileURLToPath(import.meta.resolve("decorator-transforms/runtime-esm")),
33+
},
34+
},
35+
],
36+
[
37+
"@babel/plugin-transform-runtime",
38+
{
39+
absoluteRuntime: import.meta.dirname,
40+
useESModules: true,
41+
regenerator: false,
42+
},
43+
],
44+
...babelCompatSupport(),
45+
],
46+
47+
generatorOpts: {
48+
compact: false,
49+
},
50+
};

docs-app/package.json

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
"lint:prettier": "pnpm -w exec lint prettier",
2323
"lint:types": "ember-tsc --noEmit",
2424
"start": "pnpm vite",
25-
"test": "vite build --mode test && ember test --path dist",
26-
"test:ember": "vite build --mode test && ember test --path dist"
25+
"sync": "echo 'pnpm is syncing injected deps. (this is configured in .npmrc)'",
26+
"test": "vite build --mode development && ember test --path dist",
27+
"test:ember": "vite build --mode development && ember test --path dist"
2728
},
2829
"dependencies": {
2930
"@shikijs/rehype": "^3.14.0",
30-
"@universal-ember/docs-support": "^0.6.7",
31+
"@universal-ember/docs-support": "^0.6.9",
3132
"assert": "^2.0.0",
3233
"change-case": "^5.4.4",
3334
"content-tag": "^3.1.3",
@@ -45,7 +46,7 @@
4546
"kolay": "^3.8.0",
4647
"lorem-ipsum": "^2.0.8",
4748
"path-browserify": "^1.0.1",
48-
"reactiveweb": "^1.9.0",
49+
"reactiveweb": "^1.8.0",
4950
"shiki": "^3.14.0",
5051
"should-handle-link": "^1.2.2",
5152
"tracked-built-ins": "^4.0.0"
@@ -57,6 +58,8 @@
5758
},
5859
"devDependencies": {
5960
"@babel/core": "^7.28.5",
61+
"@babel/plugin-transform-runtime": "^7.28.5",
62+
"@babel/plugin-transform-typescript": "^7.28.5",
6063
"@ember/app-tsconfig": "^1.0.3",
6164
"@ember/optional-features": "^2.2.0",
6265
"@ember/string": "^4.0.0",
@@ -68,9 +71,9 @@
6871
"@embroider/vite": "^1.3.6",
6972
"@fontsource/lexend": "^5.2.11",
7073
"@glimmer/component": "^2.0.0",
71-
"@glint/ember-tsc": "^1.0.3",
72-
"@glint/template": "^1.6.1",
73-
"@glint/tsserver-plugin": "^2.0.3",
74+
"@glint/ember-tsc": "^1.0.7",
75+
"@glint/template": "^1.7.2",
76+
"@glint/tsserver-plugin": "^2.0.7",
7477
"@nullvoxpopuli/eslint-configs": "^5.3.4",
7578
"@rollup/plugin-babel": "^6.1.0",
7679
"@tailwindcss/typography": "^0.5.15",
@@ -83,7 +86,7 @@
8386
"autoprefixer": "^10.4.19",
8487
"broccoli-asset-rev": "^3.0.0",
8588
"concurrently": "^9.2.1",
86-
"ember-a11y-testing": "^7.0.1",
89+
"ember-a11y-testing": "^7.1.3",
8790
"ember-auto-import": "^2.11.1",
8891
"ember-cli": "~6.8.0",
8992
"ember-cli-app-version": "^7.0.0",
@@ -97,12 +100,13 @@
97100
"ember-qunit": "^9.0.4",
98101
"ember-resolver": "^13.1.1",
99102
"ember-resources": "^7.0.7",
100-
"ember-source": "6.8.0",
103+
"ember-scoped-css": "^2.0.0",
104+
"ember-source": "6.8.1",
101105
"ember-template-lint": "^7.9.3",
102106
"ember-welcome-page": "^8.0.3",
103-
"eslint": "^9.38.0",
107+
"eslint": "^9.39.0",
104108
"file-loader": "^6.2.0",
105-
"globals": "^16.4.0",
109+
"globals": "^16.5.0",
106110
"globby": "^15.0.0",
107111
"inter-ui": "^4.0.2",
108112
"loader.js": "^4.7.0",

0 commit comments

Comments
 (0)