duckfn-docs-kit 0.1.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.
- package/AGENTS.md +689 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/dom.d.ts +69 -0
- package/dist/home/DfkFeatures.d.ts +20 -0
- package/dist/home/DfkHero.d.ts +25 -0
- package/dist/home/DfkNextSteps.d.ts +16 -0
- package/dist/home/styles.d.ts +8 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +2 -0
- package/dist/register-DKLiYs-F.js +2324 -0
- package/dist/register.d.ts +10 -0
- package/dist/remark.d.ts +21 -0
- package/dist/remark.js +15 -0
- package/dist/runtimeConfig-Bokbb8VH.js +106 -0
- package/dist/sql/DfkSql.d.ts +7 -0
- package/dist/sql/PreviewTabs.d.ts +37 -0
- package/dist/sql/client.d.ts +1 -0
- package/dist/sql/client.js +4 -0
- package/dist/sql/editor.d.ts +16 -0
- package/dist/sql/extensions.d.ts +108 -0
- package/dist/sql/extensions.js +198 -0
- package/dist/sql/remark.d.ts +88 -0
- package/dist/sql/remark.js +69 -0
- package/dist/sql/renderers.d.ts +44 -0
- package/dist/sql/runtime.d.ts +105 -0
- package/dist/sql/runtimeConfig.d.ts +80 -0
- package/dist/sql/styles.d.ts +6 -0
- package/dist/toc-toggle/TocToggle.d.ts +46 -0
- package/dist/toc-toggle/TocToggle.js +69 -0
- package/dist/toc-toggle/client.d.ts +1 -0
- package/dist/toc-toggle/client.js +9 -0
- package/dist/toc-toggle/plugin.d.ts +36 -0
- package/dist/toc-toggle/plugin.js +13 -0
- package/dist/types.d.ts +42 -0
- package/package.json +73 -0
- package/src/dom.ts +109 -0
- package/src/home/DfkFeatures.ts +78 -0
- package/src/home/DfkHero.ts +128 -0
- package/src/home/DfkNextSteps.ts +73 -0
- package/src/home/home.css +520 -0
- package/src/home/styles.ts +28 -0
- package/src/index.ts +59 -0
- package/src/kit.css +19 -0
- package/src/register.ts +39 -0
- package/src/remark.ts +60 -0
- package/src/sql/DfkSql.css +226 -0
- package/src/sql/DfkSql.ts +620 -0
- package/src/sql/PreviewTabs.ts +169 -0
- package/src/sql/client.ts +16 -0
- package/src/sql/editor.ts +75 -0
- package/src/sql/extensions.ts +470 -0
- package/src/sql/remark.ts +213 -0
- package/src/sql/renderers.ts +916 -0
- package/src/sql/runtime.ts +348 -0
- package/src/sql/runtimeConfig.ts +249 -0
- package/src/sql/sql.css +397 -0
- package/src/sql/styles.ts +24 -0
- package/src/theme/tokens.css +75 -0
- package/src/toc-toggle/TocToggle.css +69 -0
- package/src/toc-toggle/TocToggle.ts +172 -0
- package/src/toc-toggle/client.ts +20 -0
- package/src/toc-toggle/plugin.ts +54 -0
- package/src/types.ts +47 -0
- package/src/vite-env.d.ts +8 -0
package/src/dom.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM helpers shared by the custom elements.
|
|
3
|
+
*
|
|
4
|
+
* `HTMLElementBase` is the whole reason this module exists: the element classes
|
|
5
|
+
* `extend` it, but Docusaurus imports this package into Node during static
|
|
6
|
+
* prerendering, where the global `HTMLElement` does not exist and evaluating
|
|
7
|
+
* `class X extends HTMLElement` would throw. Falling back to an empty base
|
|
8
|
+
* class keeps module evaluation safe on the server; the real `HTMLElement` is
|
|
9
|
+
* picked up in the browser, where the elements are actually defined and run.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const HTMLElementBase: typeof HTMLElement =
|
|
13
|
+
typeof HTMLElement !== 'undefined'
|
|
14
|
+
? HTMLElement
|
|
15
|
+
: (class {} as unknown as typeof HTMLElement);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The tag's props that hold a value `el()` can assign as-is — `href`, `src`,
|
|
19
|
+
* `alt`, `width`, `hidden`, and so on.
|
|
20
|
+
*
|
|
21
|
+
* Methods and object-valued props are filtered out, so an options bag can never
|
|
22
|
+
* carry `appendChild`, `style`, `dataset` or `classList`. Getter-only props
|
|
23
|
+
* (`origin`, `clientWidth`) do pass the filter; assigning one throws in strict
|
|
24
|
+
* mode, which is loud enough to be caught the first time it runs.
|
|
25
|
+
*/
|
|
26
|
+
type ValueProps<T> = {
|
|
27
|
+
[P in keyof T as T[P] extends Function
|
|
28
|
+
? never
|
|
29
|
+
: T[P] extends object
|
|
30
|
+
? never
|
|
31
|
+
: P]?: T[P];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Options for `el()`: the tag's own props, narrowed to that tag, plus two
|
|
36
|
+
* shorthands and the attribute escape hatch.
|
|
37
|
+
*/
|
|
38
|
+
export type ElOptions<T extends Element> = ValueProps<T> & {
|
|
39
|
+
/** Shorthand for `className`. */
|
|
40
|
+
class?: string;
|
|
41
|
+
/** Shorthand for `textContent`. */
|
|
42
|
+
text?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Attributes that have no matching prop: `aria-*`, `data-*`, and
|
|
45
|
+
* custom-element attributes. Everything else goes through a property, so a
|
|
46
|
+
* boolean or a number keeps its real type instead of being stringified.
|
|
47
|
+
*/
|
|
48
|
+
attrs?: Record<string, string>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* `document.createElement` with per-tag typed options and an optional `init`
|
|
53
|
+
* callback.
|
|
54
|
+
*
|
|
55
|
+
* The one sanctioned way to build nodes in this package: it returns a live
|
|
56
|
+
* element (held in a class field by the caller), never an HTML string.
|
|
57
|
+
*
|
|
58
|
+
* The options are narrowed to the tag, so a typo, a wrong value type, a method
|
|
59
|
+
* name or an object-valued prop is a compile error:
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* el('img', {class: 'dfk-logo', alt: '', width: 480});
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* `init` describes a subtree in place, for structure that is never referenced
|
|
66
|
+
* again and therefore needs no field:
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* this.root.append(
|
|
70
|
+
* el('span', {class: 'dfk-next-card-body'}, (body) => body.append(this.#title, this.#details)),
|
|
71
|
+
* this.#arrow,
|
|
72
|
+
* );
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* Nodes touched later stay in fields. A field initializer must not read a
|
|
76
|
+
* `#field` declared below it (initializers run in declaration order, so that is
|
|
77
|
+
* a TDZ error) — pass `init` from the constructor, where every field is ready.
|
|
78
|
+
*/
|
|
79
|
+
export function el<K extends keyof HTMLElementTagNameMap>(
|
|
80
|
+
tag: K,
|
|
81
|
+
// A function is the `init` callback, so options can be skipped:
|
|
82
|
+
// `el('span', (span) => (span.textContent = label))`.
|
|
83
|
+
options?:
|
|
84
|
+
| ElOptions<HTMLElementTagNameMap[K]>
|
|
85
|
+
| ((node: HTMLElementTagNameMap[K]) => void),
|
|
86
|
+
init?: (node: HTMLElementTagNameMap[K]) => void,
|
|
87
|
+
): HTMLElementTagNameMap[K] {
|
|
88
|
+
const node = document.createElement(tag);
|
|
89
|
+
const build = typeof options === 'function' ? options : init;
|
|
90
|
+
if (typeof options !== 'function' && options) {
|
|
91
|
+
const {class: className, text, attrs, ...props} = options;
|
|
92
|
+
if (className !== undefined) {
|
|
93
|
+
node.className = className;
|
|
94
|
+
}
|
|
95
|
+
if (text !== undefined) {
|
|
96
|
+
node.textContent = text;
|
|
97
|
+
}
|
|
98
|
+
for (const [name, value] of Object.entries(props)) {
|
|
99
|
+
if (value !== undefined) {
|
|
100
|
+
(node as unknown as Record<string, unknown>)[name] = value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const [name, value] of Object.entries(attrs ?? {})) {
|
|
104
|
+
node.setAttribute(name, value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
build?.(node);
|
|
108
|
+
return node;
|
|
109
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type {IconifyIconHTMLElement} from 'iconify-icon';
|
|
2
|
+
import {el, HTMLElementBase} from '../dom';
|
|
3
|
+
import {homeStyles} from './styles';
|
|
4
|
+
import type {FeatureItem} from '../types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<dfk-features>` — the "Why duckfn" grid of feature cards. Ported from the
|
|
8
|
+
* home page's `Features()`.
|
|
9
|
+
*
|
|
10
|
+
* Retained-mode: the grid is built once and each card (a {@link DfkFeatureCard})
|
|
11
|
+
* holds its own nodes. `setFeatures()` grows or shrinks the list to the new
|
|
12
|
+
* length and mutates the cards in place — the grid is never cleared and rebuilt.
|
|
13
|
+
*
|
|
14
|
+
* The tree lives in a shadow root (adopting the shared `homeStyles()` sheet);
|
|
15
|
+
* see `DfkHero` for why the theme crosses the boundary through custom
|
|
16
|
+
* properties.
|
|
17
|
+
*/
|
|
18
|
+
export class DfkFeatures extends HTMLElementBase {
|
|
19
|
+
readonly #section = el('section', {class: 'dfk-section'});
|
|
20
|
+
readonly #inner = el('div', {class: 'dfk-section-inner'});
|
|
21
|
+
readonly #heading = el('h2', {class: 'dfk-section-title'});
|
|
22
|
+
readonly #grid = el('div', {class: 'dfk-feature-grid'});
|
|
23
|
+
readonly #cards: DfkFeatureCard[] = [];
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super();
|
|
27
|
+
this.#inner.append(this.#heading, this.#grid);
|
|
28
|
+
this.#section.appendChild(this.#inner);
|
|
29
|
+
const shadow = this.attachShadow({mode: 'open'});
|
|
30
|
+
shadow.adoptedStyleSheets = [homeStyles()];
|
|
31
|
+
shadow.appendChild(this.#section);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setSectionTitle(text: string): void {
|
|
35
|
+
this.#heading.textContent = text;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setFeatures(items: readonly FeatureItem[]): void {
|
|
39
|
+
while (this.#cards.length > items.length) {
|
|
40
|
+
this.#cards.pop()?.root.remove();
|
|
41
|
+
}
|
|
42
|
+
while (this.#cards.length < items.length) {
|
|
43
|
+
const card = new DfkFeatureCard();
|
|
44
|
+
this.#cards.push(card);
|
|
45
|
+
this.#grid.appendChild(card.root);
|
|
46
|
+
}
|
|
47
|
+
items.forEach((item, index) => this.#cards[index].setFeature(item));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** One feature card: an icon chip, a title and a description. Built once. */
|
|
52
|
+
class DfkFeatureCard {
|
|
53
|
+
readonly root = el('article', {class: 'dfk-feature-card'});
|
|
54
|
+
readonly #icon: IconifyIconHTMLElement = el('iconify-icon', {
|
|
55
|
+
class: 'dfk-feature-icon',
|
|
56
|
+
attrs: {'aria-hidden': 'true'},
|
|
57
|
+
});
|
|
58
|
+
readonly #title = el('h3', {class: 'dfk-feature-title'});
|
|
59
|
+
readonly #details = el('p', {class: 'dfk-feature-details'});
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
// The chip only wraps the icon and is never touched again, so it is
|
|
63
|
+
// described in place instead of being held in a field.
|
|
64
|
+
this.root.append(
|
|
65
|
+
el('span', {class: 'dfk-feature-icon-chip'}, (chip) =>
|
|
66
|
+
chip.appendChild(this.#icon),
|
|
67
|
+
),
|
|
68
|
+
this.#title,
|
|
69
|
+
this.#details,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setFeature(item: FeatureItem): void {
|
|
74
|
+
this.#icon.setAttribute('icon', item.icon);
|
|
75
|
+
this.#title.textContent = item.title;
|
|
76
|
+
this.#details.textContent = item.details;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type {IconifyIconHTMLElement} from 'iconify-icon';
|
|
2
|
+
import {el, HTMLElementBase} from '../dom';
|
|
3
|
+
import {homeStyles} from './styles';
|
|
4
|
+
import type {HeroAction, HeroBadge, HeroLink} from '../types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<dfk-hero>` — the landing hero: logo, title, tagline, the two call-to-action
|
|
8
|
+
* buttons and the badge row. Ported from the Docusaurus home page's `Hero()`.
|
|
9
|
+
*
|
|
10
|
+
* Retained-mode: every node is held in a field, the structure is assembled once
|
|
11
|
+
* in the constructor and the `set*` methods only mutate the nodes they own.
|
|
12
|
+
*
|
|
13
|
+
* The tree lives in a shadow root (adopting the shared `homeStyles()` sheet), so
|
|
14
|
+
* the site's global CSS cannot reach it; theme colours cross the boundary
|
|
15
|
+
* through the inherited `--duckfn-*` / `--ifm-*` custom properties.
|
|
16
|
+
*/
|
|
17
|
+
export class DfkHero extends HTMLElementBase {
|
|
18
|
+
readonly #section = el('section', {class: 'dfk-hero'});
|
|
19
|
+
readonly #inner = el('div', {class: 'dfk-hero-inner'});
|
|
20
|
+
readonly #stage = el('span', {class: 'dfk-logo-stage'});
|
|
21
|
+
// The <h1> spells out the name, so the logo is decorative: alt="".
|
|
22
|
+
readonly #logo = el('img', {
|
|
23
|
+
class: 'dfk-logo',
|
|
24
|
+
alt: '',
|
|
25
|
+
width: 480,
|
|
26
|
+
height: 480,
|
|
27
|
+
});
|
|
28
|
+
readonly #title = el('h1', {class: 'dfk-title'});
|
|
29
|
+
readonly #tagline = el('p', {class: 'dfk-tagline'});
|
|
30
|
+
readonly #actions = el('div', {class: 'dfk-actions'});
|
|
31
|
+
readonly #primary = el('a', {class: 'dfk-button-primary'});
|
|
32
|
+
readonly #primaryLabel = el('span');
|
|
33
|
+
readonly #secondary = el('a', {class: 'dfk-button-secondary'});
|
|
34
|
+
readonly #secondaryIcon: IconifyIconHTMLElement = el('iconify-icon', {
|
|
35
|
+
class: 'dfk-button-icon',
|
|
36
|
+
attrs: {'aria-hidden': 'true'},
|
|
37
|
+
});
|
|
38
|
+
readonly #secondaryLabel = el('span');
|
|
39
|
+
readonly #badges = el('div', {class: 'dfk-badges'});
|
|
40
|
+
readonly #badgeLinks: HTMLAnchorElement[] = [];
|
|
41
|
+
|
|
42
|
+
constructor() {
|
|
43
|
+
super();
|
|
44
|
+
this.#stage.appendChild(this.#logo);
|
|
45
|
+
this.#primary.appendChild(this.#primaryLabel);
|
|
46
|
+
this.#secondary.append(this.#secondaryIcon, this.#secondaryLabel);
|
|
47
|
+
this.#actions.append(this.#primary, this.#secondary);
|
|
48
|
+
this.#inner.append(
|
|
49
|
+
this.#stage,
|
|
50
|
+
this.#title,
|
|
51
|
+
this.#tagline,
|
|
52
|
+
this.#actions,
|
|
53
|
+
this.#badges,
|
|
54
|
+
);
|
|
55
|
+
this.#section.appendChild(this.#inner);
|
|
56
|
+
// A custom element's constructor may call `attachShadow` (it only must not
|
|
57
|
+
// add children or attributes to `this`), so the whole tree lives in the
|
|
58
|
+
// shadow root from the first moment the element exists — the setters are
|
|
59
|
+
// safe to call before the element is even connected.
|
|
60
|
+
const shadow = this.attachShadow({mode: 'open'});
|
|
61
|
+
shadow.adoptedStyleSheets = [homeStyles()];
|
|
62
|
+
shadow.appendChild(this.#section);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setLogo(src: string): void {
|
|
66
|
+
this.#logo.src = src;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setTitle(text: string): void {
|
|
70
|
+
this.#title.textContent = text;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setTagline(text: string): void {
|
|
74
|
+
this.#tagline.textContent = text;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The "Get started" button: an internal link, so it stays in the same tab. */
|
|
78
|
+
setPrimaryAction(link: HeroLink): void {
|
|
79
|
+
this.#fillLink(this.#primary, this.#primaryLabel, link, false);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The GitHub button: always external, always carries the glyph. */
|
|
83
|
+
setSecondaryAction(action: HeroAction): void {
|
|
84
|
+
this.#fillLink(this.#secondary, this.#secondaryLabel, action, true);
|
|
85
|
+
this.#secondaryIcon.setAttribute('icon', action.icon);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setBadges(badges: readonly HeroBadge[]): void {
|
|
89
|
+
// Grow/shrink the row to the new length and reuse the links already there.
|
|
90
|
+
while (this.#badgeLinks.length > badges.length) {
|
|
91
|
+
this.#badgeLinks.pop()?.remove();
|
|
92
|
+
}
|
|
93
|
+
while (this.#badgeLinks.length < badges.length) {
|
|
94
|
+
const link = el('a', {
|
|
95
|
+
class: 'dfk-badge',
|
|
96
|
+
target: '_blank',
|
|
97
|
+
rel: 'noopener noreferrer',
|
|
98
|
+
});
|
|
99
|
+
link.appendChild(el('img', {class: 'dfk-badge-image'}));
|
|
100
|
+
this.#badgeLinks.push(link);
|
|
101
|
+
this.#badges.appendChild(link);
|
|
102
|
+
}
|
|
103
|
+
badges.forEach((badge, index) => {
|
|
104
|
+
const link = this.#badgeLinks[index];
|
|
105
|
+
link.href = badge.href;
|
|
106
|
+
const image = link.firstElementChild as HTMLImageElement;
|
|
107
|
+
image.src = badge.src;
|
|
108
|
+
image.alt = badge.alt;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#fillLink(
|
|
113
|
+
anchor: HTMLAnchorElement,
|
|
114
|
+
label: HTMLElement,
|
|
115
|
+
link: HeroLink,
|
|
116
|
+
external: boolean,
|
|
117
|
+
): void {
|
|
118
|
+
anchor.href = link.href;
|
|
119
|
+
label.textContent = link.label;
|
|
120
|
+
if (external) {
|
|
121
|
+
anchor.target = '_blank';
|
|
122
|
+
anchor.rel = 'noopener noreferrer';
|
|
123
|
+
} else {
|
|
124
|
+
anchor.removeAttribute('target');
|
|
125
|
+
anchor.removeAttribute('rel');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type {IconifyIconHTMLElement} from 'iconify-icon';
|
|
2
|
+
import {el, HTMLElementBase} from '../dom';
|
|
3
|
+
import {homeStyles} from './styles';
|
|
4
|
+
import type {NextStepItem} from '../types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<dfk-next-steps>` — the "Where to go next" row of link cards. Ported from
|
|
8
|
+
* the home page's `NextSteps()`.
|
|
9
|
+
*
|
|
10
|
+
* Same retained-mode shape as `DfkFeatures`: the grid is built once, each card
|
|
11
|
+
* holds its own nodes, `setSteps()` grows/shrinks the list and mutates in place.
|
|
12
|
+
* The tree lives in a shadow root like the other `dfk-*` elements.
|
|
13
|
+
*/
|
|
14
|
+
export class DfkNextSteps extends HTMLElementBase {
|
|
15
|
+
readonly #section = el('section', {class: 'dfk-section'});
|
|
16
|
+
readonly #inner = el('div', {class: 'dfk-section-inner'});
|
|
17
|
+
readonly #heading = el('h2', {class: 'dfk-section-title'});
|
|
18
|
+
readonly #grid = el('div', {class: 'dfk-next-grid'});
|
|
19
|
+
readonly #cards: DfkNextStepCard[] = [];
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
this.#inner.append(this.#heading, this.#grid);
|
|
24
|
+
this.#section.appendChild(this.#inner);
|
|
25
|
+
const shadow = this.attachShadow({mode: 'open'});
|
|
26
|
+
shadow.adoptedStyleSheets = [homeStyles()];
|
|
27
|
+
shadow.appendChild(this.#section);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setSectionTitle(text: string): void {
|
|
31
|
+
this.#heading.textContent = text;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setSteps(items: readonly NextStepItem[]): void {
|
|
35
|
+
while (this.#cards.length > items.length) {
|
|
36
|
+
this.#cards.pop()?.root.remove();
|
|
37
|
+
}
|
|
38
|
+
while (this.#cards.length < items.length) {
|
|
39
|
+
const card = new DfkNextStepCard();
|
|
40
|
+
this.#cards.push(card);
|
|
41
|
+
this.#grid.appendChild(card.root);
|
|
42
|
+
}
|
|
43
|
+
items.forEach((item, index) => this.#cards[index].setStep(item));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One "next step" card: the whole card is the link. Built once. */
|
|
48
|
+
class DfkNextStepCard {
|
|
49
|
+
readonly root = el('a', {class: 'dfk-next-card'});
|
|
50
|
+
readonly #title = el('span', {class: 'dfk-next-card-title'});
|
|
51
|
+
readonly #details = el('span', {class: 'dfk-next-card-details'});
|
|
52
|
+
readonly #arrow: IconifyIconHTMLElement = el('iconify-icon', {
|
|
53
|
+
class: 'dfk-next-card-arrow',
|
|
54
|
+
attrs: {icon: 'lucide:arrow-right', 'aria-hidden': 'true'},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
// The body only groups the two text spans and is never touched again, so it
|
|
59
|
+
// is described in place instead of being held in a field.
|
|
60
|
+
this.root.append(
|
|
61
|
+
el('span', {class: 'dfk-next-card-body'}, (body) =>
|
|
62
|
+
body.append(this.#title, this.#details),
|
|
63
|
+
),
|
|
64
|
+
this.#arrow,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setStep(item: NextStepItem): void {
|
|
69
|
+
this.root.href = item.href;
|
|
70
|
+
this.#title.textContent = item.title;
|
|
71
|
+
this.#details.textContent = item.details;
|
|
72
|
+
}
|
|
73
|
+
}
|