@universal-ember/docs-support 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +3 -21
  3. package/declarations/{article.gts.d.ts → article.d.ts} +1 -1
  4. package/declarations/article.d.ts.map +1 -0
  5. package/declarations/{callout.gts.d.ts → callout.d.ts} +1 -1
  6. package/declarations/callout.d.ts.map +1 -0
  7. package/declarations/{errors.gts.d.ts → errors.d.ts} +1 -1
  8. package/declarations/errors.d.ts.map +1 -0
  9. package/declarations/{h2.gts.d.ts → h2.d.ts} +1 -1
  10. package/declarations/h2.d.ts.map +1 -0
  11. package/declarations/{icons.gts.d.ts → icons.d.ts} +1 -1
  12. package/declarations/icons.d.ts.map +1 -0
  13. package/declarations/{index-page.gts.d.ts → index-page.d.ts} +2 -2
  14. package/declarations/index-page.d.ts.map +1 -0
  15. package/declarations/index.d.ts +12 -0
  16. package/declarations/index.d.ts.map +1 -0
  17. package/declarations/{links.gts.d.ts → links.d.ts} +1 -1
  18. package/declarations/links.d.ts.map +1 -0
  19. package/declarations/{menu-layout.gts.d.ts → menu-layout.d.ts} +1 -1
  20. package/declarations/menu-layout.d.ts.map +1 -0
  21. package/declarations/{page-layout.gts.d.ts → page-layout.d.ts} +1 -1
  22. package/declarations/page-layout.d.ts.map +1 -0
  23. package/declarations/{shell.gts.d.ts → shell.d.ts} +1 -1
  24. package/declarations/shell.d.ts.map +1 -0
  25. package/declarations/{side-nav.gts.d.ts → side-nav.d.ts} +2 -3
  26. package/declarations/side-nav.d.ts.map +1 -0
  27. package/declarations/{text.gts.d.ts → text.d.ts} +1 -1
  28. package/declarations/text.d.ts.map +1 -0
  29. package/declarations/{theme-toggle.gts.d.ts → theme-toggle.d.ts} +1 -1
  30. package/declarations/theme-toggle.d.ts.map +1 -0
  31. package/dist/icons.js +11 -11
  32. package/dist/icons.js.map +1 -1
  33. package/dist/index.js +340 -11
  34. package/dist/index.js.map +1 -1
  35. package/dist/site-css/featured-demo.css +2 -2
  36. package/package.json +33 -42
  37. package/{dist/article.js → src/article.gts} +6 -11
  38. package/src/callout.gts +18 -0
  39. package/src/errors.gts +19 -0
  40. package/src/h2.gts +5 -0
  41. package/src/icons.gts +150 -0
  42. package/src/index-page.css +22 -0
  43. package/src/index-page.gts +41 -0
  44. package/{declarations/index.gts.d.ts → src/index.gts} +6 -7
  45. package/src/links.gts +31 -0
  46. package/src/menu-layout.gts +43 -0
  47. package/src/page-layout.gts +137 -0
  48. package/src/shell.gts +37 -0
  49. package/src/side-nav.gts +190 -0
  50. package/src/site-css/components.css +23 -0
  51. package/src/site-css/featured-demo.css +9 -0
  52. package/src/site-css/shell.css +102 -0
  53. package/src/site-css/shiki.css +27 -0
  54. package/src/site-css/site.css +77 -0
  55. package/src/text.gts +5 -0
  56. package/src/theme-toggle.css +48 -0
  57. package/src/theme-toggle.gts +36 -0
  58. package/declarations/article.gts.d.ts.map +0 -1
  59. package/declarations/callout.gts.d.ts.map +0 -1
  60. package/declarations/errors.gts.d.ts.map +0 -1
  61. package/declarations/h2.gts.d.ts.map +0 -1
  62. package/declarations/icons.gts.d.ts.map +0 -1
  63. package/declarations/index-page.gts.d.ts.map +0 -1
  64. package/declarations/index.gts.d.ts.map +0 -1
  65. package/declarations/links.gts.d.ts.map +0 -1
  66. package/declarations/menu-layout.gts.d.ts.map +0 -1
  67. package/declarations/page-layout.gts.d.ts.map +0 -1
  68. package/declarations/shell.gts.d.ts.map +0 -1
  69. package/declarations/side-nav.gts.d.ts.map +0 -1
  70. package/declarations/text.gts.d.ts.map +0 -1
  71. package/declarations/theme-toggle.gts.d.ts.map +0 -1
  72. package/dist/article.js.map +0 -1
  73. package/dist/callout.js +0 -14
  74. package/dist/callout.js.map +0 -1
  75. package/dist/errors.js +0 -10
  76. package/dist/errors.js.map +0 -1
  77. package/dist/h2.js +0 -10
  78. package/dist/h2.js.map +0 -1
  79. package/dist/index-page.js +0 -15
  80. package/dist/index-page.js.map +0 -1
  81. package/dist/links.js +0 -30
  82. package/dist/links.js.map +0 -1
  83. package/dist/menu-layout.js +0 -26
  84. package/dist/menu-layout.js.map +0 -1
  85. package/dist/page-layout.js +0 -60
  86. package/dist/page-layout.js.map +0 -1
  87. package/dist/shell.js +0 -30
  88. package/dist/shell.js.map +0 -1
  89. package/dist/side-nav.js +0 -116
  90. package/dist/side-nav.js.map +0 -1
  91. package/dist/text.js +0 -10
  92. package/dist/text.js.map +0 -1
  93. package/dist/theme-toggle.js +0 -33
  94. package/dist/theme-toggle.js.map +0 -1
package/src/icons.gts ADDED
@@ -0,0 +1,150 @@
1
+ import type { TOC } from '@ember/component/template-only';
2
+
3
+ /**
4
+ * Copied from font-awesome directly,
5
+ * but with the addition of fill="currentColor"
6
+ *
7
+ * Font Awesome Free 6.6.0 by @fontawesome
8
+ * - https://fontawesome.com License
9
+ * - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.
10
+ */
11
+ export const XTwitter: TOC<{ Element: SVGElement }> = <template>
12
+ <svg aria-hidden="true" ...attributes>
13
+ <use xlink:href="#social-xtwitter" />
14
+ </svg>
15
+ </template>;
16
+
17
+ export const Discord: TOC<{ Element: SVGElement }> = <template>
18
+ <svg aria-hidden="true" ...attributes>
19
+ <use xlink:href="#social-discord" />
20
+ </svg>
21
+ </template>;
22
+
23
+ export const Threads: TOC<{ Element: SVGElement }> = <template>
24
+ <svg aria-hidden="true" ...attributes>
25
+ <use xlink:href="#social-threads" />
26
+ </svg>
27
+ </template>;
28
+
29
+ export const BlueSky: TOC<{ Element: SVGElement }> = <template>
30
+ <svg aria-hidden="true" ...attributes>
31
+ <use xlink:href="#social-bluesky" />
32
+ </svg>
33
+ </template>;
34
+
35
+ export const Mastodon: TOC<{ Element: SVGElement }> = <template>
36
+ <svg aria-hidden="true" ...attributes>
37
+ <use xlink:href="#social-mastodon" />
38
+ </svg>
39
+ </template>;
40
+
41
+ export const GitHub: TOC<{ Element: SVGElement }> = <template>
42
+ <svg aria-hidden="true" ...attributes>
43
+ <use xlink:href="#social-github" />
44
+ </svg>
45
+ </template>;
46
+
47
+ export const Flask: TOC<{ Element: SVGElement }> = <template>
48
+ <svg
49
+ fill="currentColor"
50
+ xmlns="http://www.w3.org/2000/svg"
51
+ height="1em"
52
+ viewBox="0 0 448 512"
53
+ ...attributes
54
+ >{{!! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. }}<path
55
+ d="M288 0H160 128C110.3 0 96 14.3 96 32s14.3 32 32 32V196.8c0 11.8-3.3 23.5-9.5 33.5L10.3 406.2C3.6 417.2 0 429.7 0 442.6C0 480.9 31.1 512 69.4 512H378.6c38.3 0 69.4-31.1 69.4-69.4c0-12.8-3.6-25.4-10.3-36.4L329.5 230.4c-6.2-10.1-9.5-21.7-9.5-33.5V64c17.7 0 32-14.3 32-32s-14.3-32-32-32H288zM192 196.8V64h64V196.8c0 23.7 6.6 46.9 19 67.1L309.5 320h-171L173 263.9c12.4-20.2 19-43.4 19-67.1z"
56
+ /></svg>
57
+ </template>;
58
+
59
+ export const Menu: TOC<{ Element: SVGElement }> = <template>
60
+ <svg
61
+ aria-hidden="true"
62
+ viewBox="0 0 24 24"
63
+ fill="none"
64
+ stroke-width="2"
65
+ stroke-linecap="round"
66
+ ...attributes
67
+ >
68
+ <path d="M4 7h16M4 12h16M4 17h16" />
69
+ </svg>
70
+ </template>;
71
+
72
+ export const Sun: TOC<{ Element: SVGElement }> = <template>
73
+ <svg aria-hidden="true" viewBox="0 0 16 16" ...attributes>
74
+ <path
75
+ fill-rule="evenodd"
76
+ clip-rule="evenodd"
77
+ d="M7 1a1 1 0 0 1 2 0v1a1 1 0 1 1-2 0V1Zm4 7a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm2.657-5.657a1 1 0 0 0-1.414 0l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0 0-1.414Zm-1.415 11.313-.707-.707a1 1 0 0 1 1.415-1.415l.707.708a1 1 0 0 1-1.415 1.414ZM16 7.999a1 1 0 0 0-1-1h-1a1 1 0 1 0 0 2h1a1 1 0 0 0 1-1ZM7 14a1 1 0 1 1 2 0v1a1 1 0 1 1-2 0v-1Zm-2.536-2.464a1 1 0 0 0-1.414 0l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707a1 1 0 0 0 0-1.414Zm0-8.486A1 1 0 0 1 3.05 4.464l-.707-.707a1 1 0 0 1 1.414-1.414l.707.707ZM3 8a1 1 0 0 0-1-1H1a1 1 0 0 0 0 2h1a1 1 0 0 0 1-1Z"
78
+ />
79
+ </svg>
80
+ </template>;
81
+
82
+ export const Moon: TOC<{ Element: SVGElement }> = <template>
83
+ <svg aria-hidden="true" viewBox="0 0 16 16" ...attributes>
84
+ <path
85
+ fill-rule="evenodd"
86
+ clip-rule="evenodd"
87
+ d="M7.23 3.333C7.757 2.905 7.68 2 7 2a6 6 0 1 0 0 12c.68 0 .758-.905.23-1.332A5.989 5.989 0 0 1 5 8c0-1.885.87-3.568 2.23-4.668ZM12 5a1 1 0 0 1 1 1 1 1 0 0 0 1 1 1 1 0 1 1 0 2 1 1 0 0 0-1 1 1 1 0 1 1-2 0 1 1 0 0 0-1-1 1 1 0 1 1 0-2 1 1 0 0 0 1-1 1 1 0 0 1 1-1Z"
88
+ />
89
+ </svg>
90
+ </template>;
91
+
92
+ export const LightBulb: TOC<{ Element: SVGElement }> = <template>
93
+ <svg
94
+ aria-hidden="true"
95
+ viewBox="0 0 32 32"
96
+ fill="none"
97
+ class="[--icon-foreground:theme(colors.slate.900)] [--icon-background:theme(colors.white)]"
98
+ ...attributes
99
+ >
100
+ <defs>
101
+ <radialGradient
102
+ cx="0"
103
+ cy="0"
104
+ r="1"
105
+ gradientUnits="userSpaceOnUse"
106
+ id=":S6:-gradient"
107
+ gradientTransform="matrix(0 21 -21 0 20 11)"
108
+ >
109
+ <stop stop-color="#0EA5E9"></stop><stop stop-color="#22D3EE" offset=".527"></stop>
110
+ <stop stop-color="#818CF8" offset="1"></stop>
111
+ </radialGradient>
112
+ <radialGradient
113
+ cx="0"
114
+ cy="0"
115
+ r="1"
116
+ gradientUnits="userSpaceOnUse"
117
+ id=":S6:-gradient-dark"
118
+ gradientTransform="matrix(0 24.5001 -19.2498 0 16 5.5)"
119
+ >
120
+ <stop stop-color="#0EA5E9"></stop><stop stop-color="#22D3EE" offset=".527"></stop>
121
+ <stop stop-color="#818CF8" offset="1"></stop>
122
+ </radialGradient></defs>
123
+ <g class="dark:hidden">
124
+ <circle cx="20" cy="20" r="12" fill="url(#:S6:-gradient)"></circle>
125
+ <path
126
+ fill-rule="evenodd"
127
+ clip-rule="evenodd"
128
+ d="M20 24.995c0-1.855 1.094-3.501 2.427-4.792C24.61 18.087 26 15.07 26 12.231 26 7.133 21.523 3 16 3S6 7.133 6 12.23c0 2.84 1.389 5.857 3.573 7.973C10.906 21.494 12 23.14 12 24.995V27a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-2.005Z"
129
+ class="fill-[var(--icon-background)]"
130
+ fill-opacity="0.5"
131
+ ></path>
132
+ <path
133
+ d="M25 12.23c0 2.536-1.254 5.303-3.269 7.255l1.391 1.436c2.354-2.28 3.878-5.547 3.878-8.69h-2ZM16 4c5.047 0 9 3.759 9 8.23h2C27 6.508 21.998 2 16 2v2Zm-9 8.23C7 7.76 10.953 4 16 4V2C10.002 2 5 6.507 5 12.23h2Zm3.269 7.255C8.254 17.533 7 14.766 7 12.23H5c0 3.143 1.523 6.41 3.877 8.69l1.392-1.436ZM13 27v-2.005h-2V27h2Zm1 1a1 1 0 0 1-1-1h-2a3 3 0 0 0 3 3v-2Zm4 0h-4v2h4v-2Zm1-1a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2Zm0-2.005V27h2v-2.005h-2ZM8.877 20.921C10.132 22.136 11 23.538 11 24.995h2c0-2.253-1.32-4.143-2.731-5.51L8.877 20.92Zm12.854-1.436C20.32 20.852 19 22.742 19 24.995h2c0-1.457.869-2.859 2.122-4.074l-1.391-1.436Z"
134
+ class="fill-[var(--icon-foreground)]"
135
+ ></path>
136
+ <path
137
+ d="M20 26a1 1 0 1 0 0-2v2Zm-8-2a1 1 0 1 0 0 2v-2Zm2 0h-2v2h2v-2Zm1 1V13.5h-2V25h2Zm-5-11.5v1h2v-1h-2Zm3.5 4.5h5v-2h-5v2Zm8.5-3.5v-1h-2v1h2ZM20 24h-2v2h2v-2Zm-2 0h-4v2h4v-2Zm-1-10.5V25h2V13.5h-2Zm2.5-2.5a2.5 2.5 0 0 0-2.5 2.5h2a.5.5 0 0 1 .5-.5v-2Zm2.5 2.5a2.5 2.5 0 0 0-2.5-2.5v2a.5.5 0 0 1 .5.5h2ZM18.5 18a3.5 3.5 0 0 0 3.5-3.5h-2a1.5 1.5 0 0 1-1.5 1.5v2ZM10 14.5a3.5 3.5 0 0 0 3.5 3.5v-2a1.5 1.5 0 0 1-1.5-1.5h-2Zm2.5-3.5a2.5 2.5 0 0 0-2.5 2.5h2a.5.5 0 0 1 .5-.5v-2Zm2.5 2.5a2.5 2.5 0 0 0-2.5-2.5v2a.5.5 0 0 1 .5.5h2Z"
138
+ class="fill-[var(--icon-foreground)]"
139
+ ></path>
140
+ </g>
141
+ <g class="hidden dark:inline">
142
+ <path
143
+ fill-rule="evenodd"
144
+ clip-rule="evenodd"
145
+ d="M16 2C10.002 2 5 6.507 5 12.23c0 3.144 1.523 6.411 3.877 8.691.75.727 1.363 1.52 1.734 2.353.185.415.574.726 1.028.726H12a1 1 0 0 0 1-1v-4.5a.5.5 0 0 0-.5-.5A3.5 3.5 0 0 1 9 14.5V14a3 3 0 1 1 6 0v9a1 1 0 1 0 2 0v-9a3 3 0 1 1 6 0v.5a3.5 3.5 0 0 1-3.5 3.5.5.5 0 0 0-.5.5V23a1 1 0 0 0 1 1h.36c.455 0 .844-.311 1.03-.726.37-.833.982-1.626 1.732-2.353 2.354-2.28 3.878-5.547 3.878-8.69C27 6.507 21.998 2 16 2Zm5 25a1 1 0 0 0-1-1h-8a1 1 0 0 0-1 1 3 3 0 0 0 3 3h4a3 3 0 0 0 3-3Zm-8-13v1.5a.5.5 0 0 1-.5.5 1.5 1.5 0 0 1-1.5-1.5V14a1 1 0 1 1 2 0Zm6.5 2a.5.5 0 0 1-.5-.5V14a1 1 0 1 1 2 0v.5a1.5 1.5 0 0 1-1.5 1.5Z"
146
+ fill="url(#:S6:-gradient-dark)"
147
+ ></path>
148
+ </g>
149
+ </svg>
150
+ </template>;
@@ -0,0 +1,22 @@
1
+ .gradient-background {
2
+ background-image: linear-gradient(-45deg in oklch, #1252e3, #485de5, #7812e5, #3512c5);
3
+ background-size: 400% 400%;
4
+ animation: gradient-animation 16s ease infinite;
5
+ }
6
+
7
+ body.dark .gradient-background {
8
+ background-image: linear-gradient(-45deg in oklch, #110043, #182d75, #280065, #350076);
9
+ }
10
+
11
+ @keyframes gradient-animation {
12
+ 0% {
13
+ background-position: 0% 50%;
14
+ }
15
+ 50% {
16
+ background-position: 100% 50%;
17
+ }
18
+ 100% {
19
+ background-position: 0% 50%;
20
+ }
21
+ }
22
+
@@ -0,0 +1,41 @@
1
+ import './index-page.css';
2
+
3
+ import { Hero } from 'ember-primitives/layout/hero';
4
+
5
+ import type { TOC } from '@ember/component/template-only';
6
+
7
+ export const IndexPage: TOC<{
8
+ Blocks: {
9
+ logo: [];
10
+ header: [];
11
+ tagline: [];
12
+ callToAction: [];
13
+ content: [];
14
+ footer: [];
15
+ };
16
+ }> = <template>
17
+ <Hero class="shadow-xl shadow-slate-900/5 gradient-background">
18
+ <header class="absolute md:sticky right-0 bottom-0 md:top-0 z-50 p-4 flex items-center">
19
+ {{yield to="header"}}
20
+ </header>
21
+
22
+ <div class="h-full flex flex-col gap-8 justify-center items-center">
23
+ <div style="width: 66%; margin: 0 auto; transform: translateY(-20%);" class="grid gap-4">
24
+ <h1 style="filter: drop-shadow(3px 5px 0px rgba(0, 0, 0, 0.4));">
25
+ {{yield to="logo"}}
26
+ </h1>
27
+ <p class="italic text-white w-full md:w-1/2 mx-auto">
28
+ {{yield to="tagline"}}
29
+ </p>
30
+ </div>
31
+ {{yield to="callToAction"}}
32
+ </div>
33
+ </Hero>
34
+
35
+ {{yield to="content"}}
36
+
37
+ <hr />
38
+ <footer style="padding: 3rem; width: 66%;" class="mx-auto gap-12 flex-wrap flex justify-between">
39
+ {{yield to="footer"}}
40
+ </footer>
41
+ </template>;
@@ -1,12 +1,11 @@
1
- export { H2 } from './h2.gts';
2
- export { Callout } from './callout.gts';
3
- export { Text } from './text.gts';
4
1
  export { Article } from './article.gts';
5
- export { Shell } from './shell.gts';
6
- export { IndexPage } from './index-page.gts';
7
- export { Link, InternalLink } from './links.gts';
2
+ export { Callout } from './callout.gts';
8
3
  export { OopsError } from './errors.gts';
4
+ export { H2 } from './h2.gts';
5
+ export { IndexPage } from './index-page.gts';
6
+ export { InternalLink,Link } from './links.gts';
9
7
  export { ResponsiveMenuLayout } from './menu-layout.gts';
10
8
  export { PageLayout, TopRight } from './page-layout.gts';
9
+ export { Shell } from './shell.gts';
10
+ export { Text } from './text.gts';
11
11
  export { ThemeToggle } from './theme-toggle.gts';
12
- //# sourceMappingURL=index.gts.d.ts.map
package/src/links.gts ADDED
@@ -0,0 +1,31 @@
1
+ import { ExternalLink } from 'ember-primitives/components/external-link';
2
+
3
+ import type { TOC } from '@ember/component/template-only';
4
+
5
+ const linkClasses = `
6
+ text-sm font-semibold
7
+ dark:text-sky-400
8
+ no-underline shadow-[inset_0_-2px_0_0_var(--tw-prose-background,#fff),inset_0_calc(-1*(var(--tw-prose-underline-size,4px)+2px))_0_0_var(--tw-prose-underline,theme(colors.sky.300))]
9
+ hover:[--tw-prose-underline-size:6px]
10
+ dark:[--tw-prose-background:theme(colors.slate.900)]
11
+ dark:shadow-[inset_0_calc(-1*var(--tw-prose-underline-size,2px))_0_0_var(--tw-prose-underline,theme(colors.sky.800))]
12
+ dark:hover:[--tw-prose-underline-size:6px]
13
+ `;
14
+
15
+ export const InternalLink: TOC<{
16
+ Element: HTMLAnchorElement;
17
+ Blocks: { default: [] };
18
+ }> = <template>
19
+ <a class={{linkClasses}} href="#" ...attributes>
20
+ {{yield}}
21
+ </a>
22
+ </template>;
23
+
24
+ export const Link: TOC<{
25
+ Element: HTMLAnchorElement;
26
+ Blocks: { default: [] };
27
+ }> = <template>
28
+ <ExternalLink class={{linkClasses}} ...attributes>
29
+ {{yield}}
30
+ </ExternalLink>
31
+ </template>;
@@ -0,0 +1,43 @@
1
+ // @ts-expect-error - no types are provided for ember-mobile-menu
2
+ import MenuWrapper from 'ember-mobile-menu/components/mobile-menu-wrapper';
3
+
4
+ import { Menu } from './icons.gts';
5
+ import { SideNav } from './side-nav.gts';
6
+
7
+ import type { TOC } from '@ember/component/template-only';
8
+ import type { ComponentLike, WithBoundArgs } from '@glint/template';
9
+
10
+ const Toggle: TOC<{
11
+ Args: {
12
+ toggle: ComponentLike<{ Blocks: { default: [] } }>;
13
+ };
14
+ }> = <template>
15
+ <@toggle>
16
+ <Menu class="w-6 h-6 stroke-slate-500" />
17
+ </@toggle>
18
+ </template>;
19
+
20
+ export const ResponsiveMenuLayout: TOC<{
21
+ Blocks: {
22
+ content: [];
23
+ header: [toggle: WithBoundArgs<typeof Toggle, 'toggle'>];
24
+ };
25
+ }> = <template>
26
+ <MenuWrapper as |mmw|>
27
+ <mmw.MobileMenu @mode="push" @maxWidth={{300}} as |mm|>
28
+ <SideNav @onClick={{mm.actions.close}} />
29
+ </mmw.MobileMenu>
30
+
31
+ <mmw.Content>
32
+ {{yield (component Toggle toggle=mmw.Toggle) to="header"}}
33
+
34
+ <div class="outer-content">
35
+ <SideNav />
36
+
37
+ <main class="relative grid justify-center flex-auto w-full mx-auto max-w-8xl">
38
+ {{yield to="content"}}
39
+ </main>
40
+ </div>
41
+ </mmw.Content>
42
+ </MenuWrapper>
43
+ </template>;
@@ -0,0 +1,137 @@
1
+ import { modifier } from 'ember-modifier';
2
+ import { cell } from 'ember-resources';
3
+ import { Page } from 'kolay/components';
4
+
5
+ import { Article } from './article.gts';
6
+ import { Link } from './links.gts';
7
+ import { ResponsiveMenuLayout } from './menu-layout.gts';
8
+ import { ThemeToggle } from './theme-toggle.gts';
9
+
10
+ import type { TOC } from '@ember/component/template-only';
11
+
12
+ // Removes the App Shell / welcome UI
13
+ // before initial rending and chunk loading finishes
14
+ function removeLoader() {
15
+ document.querySelector('#initial-loader')?.remove();
16
+ }
17
+
18
+
19
+ function resetScroll(..._args: unknown[]) {
20
+ document.querySelector('html')?.scrollTo(0, 0);
21
+ }
22
+
23
+ const isScrolled = cell(false);
24
+
25
+ const onWindowScroll = modifier(() => {
26
+ function onScroll() {
27
+ isScrolled.current = window.scrollY > 0;
28
+ }
29
+
30
+ onScroll();
31
+ window.addEventListener('scroll', onScroll, { passive: true });
32
+
33
+ return () => {
34
+ window.removeEventListener('scroll', onScroll);
35
+ };
36
+ });
37
+
38
+ export const PageLayout: TOC<{
39
+ Blocks: {
40
+ logoLink: [];
41
+ topRight: [];
42
+ editLink: [typeof EditLink];
43
+ error: [error: string];
44
+ };
45
+ }> = <template>
46
+ <ResponsiveMenuLayout>
47
+ <:header as |Toggle|>
48
+ <header
49
+ class="sticky top-0 z-50 transition duration-500 shadow-md shadow-slate-900/5 dark:shadow-none bg-white/95
50
+ {{if
51
+ isScrolled.current
52
+ 'dark:bg-slate-900/95 dark:backdrop-blur dark:[@supports(backdrop-filter:blur(0))]:bg-slate-900/75'
53
+ 'dark:bg-slate-900/95'
54
+ }}"
55
+ {{onWindowScroll}}
56
+ >
57
+ <div class="outer-content flex flex-none flex-wrap items-center justify-between py-4">
58
+ <div class="flex mr-6 lg:hidden">
59
+ <Toggle />
60
+ </div>
61
+ <div class="relative flex items-center flex-grow basis-0">
62
+ <a href="/" aria-label="Home page">
63
+ {{yield to="logoLink"}}
64
+ </a>
65
+ </div>
66
+ {{!
67
+ If we ever have a search bar
68
+ <div class="mr-6 -my-5 sm:mr-8 md:mr-0">
69
+ input here
70
+ </div>
71
+ }}
72
+ <TopRight>
73
+ {{yield to="topRight"}}
74
+ </TopRight>
75
+ </div>
76
+ </header>
77
+ </:header>
78
+ <:content>
79
+ <section data-main-scroll-container class="flex-auto max-w-2xl min-w-0 py-4 lg:max-w-none">
80
+ <Article>
81
+ <Page>
82
+ <:pending>
83
+ <div
84
+ class="fixed top-12 p-4 rounded z-50 transition border border-slate-800 duration-500 shadow-xl shadow-slate-900/5 bg-white/95 'dark:bg-slate-900/95 dark:backdrop-blur dark:[@supports(backdrop-filter:blur(0))]:bg-slate-900/75'"
85
+ >
86
+ Loading, Compiling, etc
87
+ </div>
88
+ </:pending>
89
+
90
+ <:error as |error|>
91
+ <section>
92
+ {{yield error to="error"}}
93
+ </section>
94
+ </:error>
95
+
96
+ <:success as |prose|>
97
+ <prose />
98
+ {{(removeLoader)}}
99
+ {{! this is probably really bad, and anti-patterny
100
+ but ember doesn't have a good way to have libraries
101
+ tie in to the URL without a bunch of setup -- which is maybe fine?
102
+ needs some experimenting -- there is a bit of a disconnect with
103
+ deriving data from the URL, and the timing of the model hooks.
104
+ It might be possible to have an afterModel hook wait until the page is
105
+ compiled.
106
+ (that's why we have async state, because we're compiling, not loading)
107
+ }}
108
+ {{resetScroll prose}}
109
+ </:success>
110
+ </Page>
111
+ </Article>
112
+
113
+ {{#if (has-block "editLink")}}
114
+
115
+ <div class="flex justify-end pt-6 mt-12 border-t border-slate-200 dark:border-slate-800">
116
+
117
+ {{yield EditLink to="editLink"}}
118
+ </div>
119
+ {{/if}}
120
+ </section>
121
+ </:content>
122
+
123
+ </ResponsiveMenuLayout>
124
+ </template>;
125
+
126
+ const EditLink: TOC<{ Args: { href: string }; Blocks: { default: [] } }> = <template>
127
+ <Link class="edit-page flex" href={{@href}}>
128
+ {{yield}}
129
+ </Link>
130
+ </template>;
131
+
132
+ export const TopRight: TOC<{ Blocks: { default: [] } }> = <template>
133
+ <div class="relative flex justify-end gap-6 basis-0 sm:gap-8 md:flex-grow">
134
+ <ThemeToggle />
135
+ {{yield}}
136
+ </div>
137
+ </template>;
package/src/shell.gts ADDED
@@ -0,0 +1,37 @@
1
+ import 'ember-mobile-menu/themes/android';
2
+ /**
3
+ * NOTE: while this project uses tailwind,
4
+ * CSS is better than tailwind for
5
+ * - global styles
6
+ * - styling some recursive structures generated by other libraries.
7
+ *
8
+ * Using both together is *very nice* 🎉
9
+ */
10
+ import './site-css/site.css';
11
+ import './site-css/components.css';
12
+ import './site-css/featured-demo.css';
13
+ import './site-css/shiki.css';
14
+ import './site-css/shell.css';
15
+
16
+ import { colorScheme } from 'ember-primitives/color-scheme';
17
+
18
+ import type { TOC } from '@ember/component/template-only';
19
+
20
+ export const Shell: TOC<{ Blocks: { default: [] } }> = <template>
21
+ {{(syncBodyClass)}}
22
+ {{yield}}
23
+ </template>;
24
+
25
+ function syncBodyClass() {
26
+ if (colorScheme.isDark) {
27
+ document.body.classList.add('dark');
28
+ document.body.classList.add('theme-dark');
29
+ document.body.classList.remove('theme-light');
30
+ document.body.classList.remove('light');
31
+ } else {
32
+ document.body.classList.remove('theme-dark');
33
+ document.body.classList.remove('dark');
34
+ document.body.classList.add('theme-light');
35
+ document.body.classList.add('light');
36
+ }
37
+ }
@@ -0,0 +1,190 @@
1
+ import Component from '@glimmer/component';
2
+ import { on } from '@ember/modifier';
3
+ import { service } from '@ember/service';
4
+
5
+ import { sentenceCase } from 'change-case';
6
+ import { link } from 'ember-primitives/helpers';
7
+ import { selected } from 'kolay';
8
+ import { PageNav } from 'kolay/components';
9
+ import { getAnchor } from 'should-handle-link';
10
+
11
+ import type { TOC } from '@ember/component/template-only';
12
+ import type RouterService from '@ember/routing/router-service';
13
+ import type { Page } from 'kolay';
14
+
15
+ type CustomPage = Page & {
16
+ title?: string;
17
+ };
18
+
19
+ function fixWords(text: string) {
20
+ switch (text.toLowerCase()) {
21
+ case 'ui':
22
+ return 'UI';
23
+ case 'iframe':
24
+ return 'IFrame';
25
+ default:
26
+ return text;
27
+ }
28
+ }
29
+
30
+ const joinUrl = (...strs: string[]) => {
31
+ const prefix = strs[0]?.startsWith('/') ? '/' : '';
32
+
33
+ return (
34
+ prefix +
35
+ strs
36
+ .map((s) => s.replace(/^\//, '').replace(/\/$/, ''))
37
+ .filter((x) => !!x)
38
+ .join('/')
39
+ );
40
+ };
41
+
42
+ /**
43
+ * Converts 1-2-hyphenated-thing
44
+ * to
45
+ * Hyphenated Thing
46
+ */
47
+ const titleize = (str: string) => {
48
+ return (
49
+ str
50
+ .split(/-|\s/)
51
+ .filter(Boolean)
52
+ .filter((text) => !text.match(/^[\d]+$/))
53
+ .map((text) => `${text[0]?.toLocaleUpperCase()}${text.slice(1, text.length)}`)
54
+ .map((text) => fixWords(text))
55
+ .join(' ')
56
+ .split('.')[0] || ''
57
+ );
58
+ };
59
+
60
+ function nameFor(x: Page): string {
61
+ if ('componentName' in x) {
62
+ return String(x.componentName);
63
+ }
64
+
65
+ const page = x as CustomPage;
66
+
67
+ return page.title ? page.title : sentenceCase(page.name);
68
+ }
69
+
70
+ const asComponent = (str: string) => {
71
+ return `<${str.split('.')[0]?.replaceAll(' ', '')} />`;
72
+ };
73
+
74
+ const isComponents = (str: string) => str === 'components';
75
+
76
+ const SectionLink: TOC<{ Element: HTMLAnchorElement; Args: { href: string; name: string } }> =
77
+ <template>
78
+ {{#let (link @href) as |l|}}
79
+ <a
80
+ href={{@href}}
81
+ class="font-medium font-display
82
+ {{if
83
+ l.isActive
84
+ 'text-sky-500'
85
+ 'text-slate-900 hover:text-slate-600 dark:text-white dark:hover:text-slate-300'
86
+ }}"
87
+ {{on "click" l.handleClick}}
88
+ ...attributes
89
+ >
90
+ {{#if (isComponents @name)}}
91
+ {{asComponent (titleize @name)}}
92
+ {{else}}
93
+ {{titleize @name}}
94
+ {{/if}}
95
+ </a>
96
+ {{/let}}
97
+ </template>;
98
+
99
+ const SubSectionLink: TOC<{ Element: HTMLAnchorElement; Args: { href: string; name: string } }> =
100
+ <template>
101
+ {{#let (link @href) as |l|}}
102
+ <a
103
+ href={{@href}}
104
+ class="block w-full before:pointer-events-none before:absolute before:-left-1 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full
105
+ {{if
106
+ l.isActive
107
+ 'font-semibold text-sky-500 before:bg-sky-500'
108
+ 'text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300'
109
+ }}"
110
+ {{on "click" l.handleClick}}
111
+ ...attributes
112
+ >
113
+ {{#if (isComponents @name)}}
114
+ {{asComponent (titleize @name)}}
115
+ {{else}}
116
+ {{titleize @name}}
117
+ {{/if}}
118
+ </a>
119
+ {{/let}}
120
+ </template>;
121
+
122
+ export class SideNav extends Component<{
123
+ Element: HTMLElement;
124
+ Args: {
125
+ onClick?: () => void;
126
+ };
127
+ }> {
128
+ get #selected() {
129
+ return selected(this);
130
+ }
131
+ @service('router') declare router: RouterService;
132
+
133
+ get humanSelected() {
134
+ const path = this.#selected?.path;
135
+
136
+ if (!path) return undefined;
137
+
138
+ return path.split('/').filter(Boolean).map(titleize).join(' / ');
139
+ }
140
+
141
+ get rootUrl() {
142
+ return this.router.rootURL;
143
+ }
144
+
145
+ closeNav = (event: Event) => {
146
+ if (!getAnchor(event)) return;
147
+
148
+ this.args.onClick?.();
149
+ };
150
+
151
+ /**
152
+ *
153
+ * This nav needs an aria-label to get around
154
+ * "Ensure landmarks are unique"
155
+ * because some demos render navs, and it's important that those
156
+ * demos are as simple as possible.
157
+ *
158
+ *
159
+ * nav isn't actually made in to an interactive element,
160
+ * it's an event delegation handler.
161
+ * The links themselves remain the actual interactive elements.
162
+ */
163
+ <template>
164
+ <aside class="bg-white dark:bg-slate-900" ...attributes>
165
+ <PageNav aria-label="Main Navigation">
166
+ <:page as |x|>
167
+ <SubSectionLink
168
+ @href={{joinUrl this.rootUrl x.page.path}}
169
+ @name={{nameFor x.page}}
170
+ {{on "click" this.closeNav}}
171
+ />
172
+ </:page>
173
+
174
+ <:collection as |x|>
175
+ {{#if x.index}}
176
+ <SectionLink
177
+ @href={{joinUrl this.rootUrl x.index.page.path}}
178
+ @name={{titleize x.collection.name}}
179
+ {{on "click" this.closeNav}}
180
+ />
181
+ {{else}}
182
+ <h2>
183
+ {{titleize x.collection.name}}
184
+ </h2>
185
+ {{/if}}
186
+ </:collection>
187
+ </PageNav>
188
+ </aside>
189
+ </template>
190
+ }