@fuzdev/fuz_ui 0.210.4 → 0.212.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/dist/Alert.svelte +17 -14
- package/dist/Alert.svelte.d.ts +5 -1
- package/dist/Alert.svelte.d.ts.map +1 -1
- package/dist/Breadcrumb.svelte +14 -2
- package/dist/Breadcrumb.svelte.d.ts +6 -0
- package/dist/Breadcrumb.svelte.d.ts.map +1 -1
- package/dist/Card.svelte +58 -49
- package/dist/Card.svelte.d.ts +19 -5
- package/dist/Card.svelte.d.ts.map +1 -1
- package/dist/ContextmenuLinkEntry.svelte +2 -1
- package/dist/ContextmenuLinkEntry.svelte.d.ts.map +1 -1
- package/dist/Docs.svelte +48 -7
- package/dist/Docs.svelte.d.ts +17 -0
- package/dist/Docs.svelte.d.ts.map +1 -1
- package/dist/DocsSecondaryNav.svelte +9 -0
- package/dist/DocsSecondaryNav.svelte.d.ts +5 -0
- package/dist/DocsSecondaryNav.svelte.d.ts.map +1 -1
- package/dist/alert.d.ts +1 -1
- package/dist/alert.d.ts.map +1 -1
- package/dist/page_helpers.d.ts +16 -0
- package/dist/page_helpers.d.ts.map +1 -0
- package/dist/page_helpers.js +23 -0
- package/package.json +1 -1
- package/src/lib/alert.ts +1 -1
- package/src/lib/page_helpers.ts +27 -0
package/dist/Alert.svelte
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
div_attrs,
|
|
23
23
|
children,
|
|
24
24
|
...rest
|
|
25
|
-
}: // generic element attrs, the common denominator of the rendered roots
|
|
25
|
+
}: // generic element attrs, the common denominator of the rendered roots;
|
|
26
|
+
// branch-specific attributes go in `button_attrs` and `div_attrs`
|
|
26
27
|
HTMLAttributes<HTMLElement> & {
|
|
27
28
|
status?: AlertStatus;
|
|
28
29
|
color?: string;
|
|
@@ -32,7 +33,11 @@
|
|
|
32
33
|
/** Renders the alert as a `<button>` when provided. */
|
|
33
34
|
onclick?: (() => void) | undefined;
|
|
34
35
|
disabled?: boolean;
|
|
35
|
-
|
|
36
|
+
/**
|
|
37
|
+
* A decorative icon, hidden from assistive tech: a string, or a snippet given the
|
|
38
|
+
* status's icon. Defaults to the status's icon, and `null` or an empty string is none.
|
|
39
|
+
*/
|
|
40
|
+
icon?: string | Snippet<[icon: string]> | null | undefined;
|
|
36
41
|
/** Button attributes, applied only when `onclick` renders the alert as a `<button>`. */
|
|
37
42
|
button_attrs?: SvelteHTMLElements['button'];
|
|
38
43
|
/** Div attributes, applied only when the alert renders as a `<div>` (no `onclick`). */
|
|
@@ -44,9 +49,7 @@
|
|
|
44
49
|
// TODO change this to use the hue and put transparency on the borders, or add a borderColor option
|
|
45
50
|
const { color: status_color, icon: status_icon } = $derived(options);
|
|
46
51
|
const final_color = $derived(color ?? status_color);
|
|
47
|
-
const final_icon = $derived(
|
|
48
|
-
typeof icon === 'string' ? icon : (status_icon ?? alert_status_options.inform.icon!)
|
|
49
|
-
);
|
|
52
|
+
const final_icon = $derived(typeof icon === 'string' ? icon : status_icon);
|
|
50
53
|
</script>
|
|
51
54
|
|
|
52
55
|
{#if onclick}
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
{onclick}
|
|
60
63
|
disabled={disabled ?? button_attrs?.disabled}
|
|
61
64
|
>
|
|
62
|
-
{@render
|
|
65
|
+
{@render alert_content()}
|
|
63
66
|
</button>
|
|
64
67
|
{:else}
|
|
65
68
|
<div
|
|
@@ -69,21 +72,21 @@
|
|
|
69
72
|
class="alert panel {rest.class} {div_attrs?.class}"
|
|
70
73
|
style:--text_color={final_color}
|
|
71
74
|
>
|
|
72
|
-
{@render
|
|
75
|
+
{@render alert_content()}
|
|
73
76
|
</div>
|
|
74
77
|
{/if}
|
|
75
78
|
|
|
76
|
-
{#snippet
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{:else}
|
|
79
|
+
{#snippet alert_content()}
|
|
80
|
+
<!-- the status's icon by default, or one given, and none for `null` or an empty string -->
|
|
81
|
+
{#if icon === undefined || icon}
|
|
82
|
+
<div class="icon" aria-hidden="true">
|
|
83
|
+
{#if typeof icon === 'function'}
|
|
82
84
|
{@render icon(final_icon)}
|
|
85
|
+
{:else}
|
|
86
|
+
{final_icon}
|
|
83
87
|
{/if}
|
|
84
88
|
</div>
|
|
85
89
|
{/if}
|
|
86
|
-
<!-- TODO conflict in the names of content as the snippet and this inner div -->
|
|
87
90
|
<div class="content">
|
|
88
91
|
{@render children()}
|
|
89
92
|
</div>
|
package/dist/Alert.svelte.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
3
3
|
import { type AlertStatus } from './alert.js';
|
|
4
|
-
type $$ComponentProps = // generic element attrs, the common denominator of the rendered roots
|
|
4
|
+
type $$ComponentProps = // generic element attrs, the common denominator of the rendered roots;
|
|
5
5
|
HTMLAttributes<HTMLElement> & {
|
|
6
6
|
status?: AlertStatus;
|
|
7
7
|
color?: string;
|
|
8
8
|
/** Renders the alert as a `<button>` when provided. */
|
|
9
9
|
onclick?: (() => void) | undefined;
|
|
10
10
|
disabled?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* A decorative icon, hidden from assistive tech: a string, or a snippet given the
|
|
13
|
+
* status's icon. Defaults to the status's icon, and `null` or an empty string is none.
|
|
14
|
+
*/
|
|
11
15
|
icon?: string | Snippet<[icon: string]> | null | undefined;
|
|
12
16
|
/** Button attributes, applied only when `onclick` renders the alert as a `<button>`. */
|
|
13
17
|
button_attrs?: SvelteHTMLElements['button'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Alert.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEnE,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"Alert.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Alert.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEnE,KAAK,gBAAgB,GAEpB,AAFwB,uEAAuE;AAE/F,cAAc,CAAC,WAAW,CAAC,GAAG;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IAIf,uDAAuD;IACvD,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3D,wFAAwF;IACxF,YAAY,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC5C,uFAAuF;IACvF,SAAS,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;CAClB,CAAC;AAgEJ,QAAA,MAAM,KAAK,sDAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
|
package/dist/Breadcrumb.svelte
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
const {
|
|
13
13
|
path,
|
|
14
14
|
selected_path,
|
|
15
|
+
root_path,
|
|
15
16
|
base_path,
|
|
16
17
|
separator,
|
|
17
18
|
children,
|
|
@@ -26,6 +27,12 @@
|
|
|
26
27
|
* `null` means none and `undefined` is detected from the current url.
|
|
27
28
|
*/
|
|
28
29
|
selected_path?: string | null | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Where the root link goes, prefixed with a slash and relative to the base path,
|
|
32
|
+
* e.g. `'/docs'` on a home page, where linking to the root would link to itself.
|
|
33
|
+
* Defaults to the root.
|
|
34
|
+
*/
|
|
35
|
+
root_path?: string;
|
|
29
36
|
/**
|
|
30
37
|
* Sets a custom base path of `path` and `selected_path`.
|
|
31
38
|
* Defaults to `base` from `$app/paths`.
|
|
@@ -53,7 +60,9 @@
|
|
|
53
60
|
|
|
54
61
|
const path_pieces = $derived(parse_path_pieces(final_path));
|
|
55
62
|
|
|
56
|
-
const
|
|
63
|
+
const root_href = $derived(
|
|
64
|
+
root_path === undefined ? ensure_end(final_base_path, '/') : final_base_path + root_path
|
|
65
|
+
);
|
|
57
66
|
|
|
58
67
|
// TODO animate these
|
|
59
68
|
// `transition:slide={{axis: 'x'}}`
|
|
@@ -61,7 +70,7 @@
|
|
|
61
70
|
|
|
62
71
|
<div {...rest} class="breadcrumb {rest.class}">
|
|
63
72
|
<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
|
|
64
|
-
<a href={
|
|
73
|
+
<a href={root_href} class={{ selected: root_href === final_base_path + final_selected_path }}>
|
|
65
74
|
{#if children}
|
|
66
75
|
{@render children()}
|
|
67
76
|
{:else if icon_data}
|
|
@@ -94,12 +103,15 @@
|
|
|
94
103
|
align-items: center;
|
|
95
104
|
font-size: var(--font_size, var(--font_size_md));
|
|
96
105
|
}
|
|
106
|
+
/* at least one line of text tall, so a root link holding only an icon is as tall as a
|
|
107
|
+
* breadcrumb with path pieces, and the layout around it doesn't shift between pages */
|
|
97
108
|
a {
|
|
98
109
|
position: relative;
|
|
99
110
|
z-index: 1;
|
|
100
111
|
padding: 0 calc(var(--font_size, var(--font_size_md)) * 0.5);
|
|
101
112
|
display: flex;
|
|
102
113
|
align-items: center;
|
|
114
|
+
min-height: 1lh;
|
|
103
115
|
}
|
|
104
116
|
.separator {
|
|
105
117
|
position: relative;
|
|
@@ -10,6 +10,12 @@ type $$ComponentProps = SvelteHTMLElements['div'] & {
|
|
|
10
10
|
* `null` means none and `undefined` is detected from the current url.
|
|
11
11
|
*/
|
|
12
12
|
selected_path?: string | null | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Where the root link goes, prefixed with a slash and relative to the base path,
|
|
15
|
+
* e.g. `'/docs'` on a home page, where linking to the root would link to itself.
|
|
16
|
+
* Defaults to the root.
|
|
17
|
+
*/
|
|
18
|
+
root_path?: string;
|
|
13
19
|
/**
|
|
14
20
|
* Sets a custom base path of `path` and `selected_path`.
|
|
15
21
|
* Defaults to `base` from `$app/paths`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumb.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Breadcrumb.svelte"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAKzD,KAAK,gBAAgB,GAAI,kBAAkB,CAAC,KAAK,CAAC,GAAG;IACpD;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;
|
|
1
|
+
{"version":3,"file":"Breadcrumb.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Breadcrumb.svelte"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAKzD,KAAK,gBAAgB,GAAI,kBAAkB,CAAC,KAAK,CAAC,GAAG;IACpD;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAuEH,QAAA,MAAM,UAAU,sDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
package/dist/Card.svelte
CHANGED
|
@@ -4,30 +4,47 @@
|
|
|
4
4
|
import type { Snippet } from 'svelte';
|
|
5
5
|
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
6
6
|
|
|
7
|
+
import { href_is_current_page } from './page_helpers.js';
|
|
8
|
+
|
|
7
9
|
// TODO think through Alert+Card APIs together, one can be a button and the other a link atm
|
|
8
10
|
|
|
9
11
|
const {
|
|
10
12
|
href,
|
|
11
13
|
tag,
|
|
12
|
-
align = '
|
|
14
|
+
align = 'start',
|
|
13
15
|
icon,
|
|
14
16
|
a_attrs,
|
|
15
17
|
children,
|
|
16
18
|
...rest
|
|
17
|
-
}: // generic element attrs, the common denominator of the rendered roots
|
|
19
|
+
}: // generic element attrs, the common denominator of the rendered roots;
|
|
20
|
+
// branch-specific attributes go in `a_attrs`
|
|
18
21
|
HTMLAttributes<HTMLElement> & {
|
|
19
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Renders the card as an `<a>`, marked `selected` while it leads to the current
|
|
24
|
+
* page, ignoring a trailing slash, the query, and the hash.
|
|
25
|
+
*/
|
|
20
26
|
href?: string | undefined;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
/** The element to render, defaulting to `a` with an `href` and `div` without. */
|
|
28
|
+
tag?: keyof HTMLElementTagNameMap | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The card's layout: where the icon sits, at the inline start or end beside the
|
|
31
|
+
* content or above or below it, and how the content aligns, toward the end for
|
|
32
|
+
* `end` and centered for `top` and `bottom`.
|
|
33
|
+
* @default 'start'
|
|
34
|
+
*/
|
|
35
|
+
align?: 'start' | 'end' | 'top' | 'bottom';
|
|
36
|
+
/**
|
|
37
|
+
* A decorative icon, hidden from assistive tech: a string like an emoji or a glyph,
|
|
38
|
+
* or a snippet for anything else. None when absent, null, or empty.
|
|
39
|
+
*/
|
|
40
|
+
icon?: string | Snippet | null;
|
|
24
41
|
/** Anchor attributes, applied only when `href` renders the card as an `<a>`. */
|
|
25
42
|
a_attrs?: SvelteHTMLElements['a'];
|
|
26
43
|
children: Snippet;
|
|
27
44
|
} = $props();
|
|
28
45
|
|
|
29
46
|
const link = $derived(!!href);
|
|
30
|
-
const selected = $derived(
|
|
47
|
+
const selected = $derived(!!href && href_is_current_page(href, page.url));
|
|
31
48
|
// the tag renders through `svelte:element`, so declare the styled one for fuz_css extraction
|
|
32
49
|
// @fuz-elements a
|
|
33
50
|
const final_tag = $derived(tag ?? (link ? 'a' : 'div'));
|
|
@@ -43,41 +60,38 @@
|
|
|
43
60
|
}
|
|
44
61
|
});
|
|
45
62
|
}
|
|
46
|
-
|
|
47
|
-
const left = $derived(align === 'left');
|
|
48
|
-
const right = $derived(align === 'right');
|
|
49
|
-
const above = $derived(align === 'above');
|
|
50
|
-
const below = $derived(align === 'below');
|
|
51
|
-
|
|
52
|
-
const final_icon: string | Snippet = $derived(icon ?? (link ? '🔗' : '🪧'));
|
|
53
63
|
</script>
|
|
54
64
|
|
|
65
|
+
<!-- a button that isn't told otherwise doesn't submit a form around it -->
|
|
55
66
|
<svelte:element
|
|
56
67
|
this={final_tag}
|
|
68
|
+
type={final_tag === 'button' ? 'button' : undefined}
|
|
57
69
|
{...rest}
|
|
58
70
|
{...inferred_attrs}
|
|
59
|
-
class={['card', rest.class, inferred_attrs?.class, { link, selected
|
|
71
|
+
class={['card', align, rest.class, inferred_attrs?.class, { link, selected }]}
|
|
60
72
|
>
|
|
61
|
-
{#if align === '
|
|
73
|
+
{#if align === 'start' || align === 'top'}
|
|
62
74
|
{@render icon_snippet()}
|
|
63
75
|
{/if}
|
|
64
76
|
<div class="content">
|
|
65
77
|
{@render children()}
|
|
66
78
|
</div>
|
|
67
|
-
{#if align === '
|
|
79
|
+
{#if align === 'end' || align === 'bottom'}
|
|
68
80
|
{@render icon_snippet()}
|
|
69
81
|
{/if}
|
|
70
82
|
</svelte:element>
|
|
71
83
|
|
|
72
|
-
<!--
|
|
84
|
+
<!-- an empty string is no icon, so no empty element takes the icon's margin -->
|
|
73
85
|
{#snippet icon_snippet()}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
{#if icon}
|
|
87
|
+
<div class="icon" aria-hidden="true">
|
|
88
|
+
{#if typeof icon === 'string'}
|
|
89
|
+
{icon}
|
|
90
|
+
{:else}
|
|
91
|
+
{@render icon()}
|
|
92
|
+
{/if}
|
|
93
|
+
</div>
|
|
94
|
+
{/if}
|
|
81
95
|
{/snippet}
|
|
82
96
|
|
|
83
97
|
<style>
|
|
@@ -92,13 +106,13 @@
|
|
|
92
106
|
background-color: var(--fg_10);
|
|
93
107
|
border-radius: var(--border_radius, var(--border_radius_md));
|
|
94
108
|
text-decoration: none;
|
|
95
|
-
text-align:
|
|
109
|
+
text-align: start;
|
|
96
110
|
}
|
|
97
|
-
.
|
|
111
|
+
.end {
|
|
98
112
|
justify-content: flex-end;
|
|
99
113
|
}
|
|
100
|
-
.
|
|
101
|
-
.
|
|
114
|
+
.top,
|
|
115
|
+
.bottom {
|
|
102
116
|
flex-direction: column;
|
|
103
117
|
text-align: center;
|
|
104
118
|
}
|
|
@@ -128,12 +142,15 @@
|
|
|
128
142
|
.link:hover .content {
|
|
129
143
|
text-decoration: underline;
|
|
130
144
|
}
|
|
131
|
-
.
|
|
132
|
-
|
|
145
|
+
.end .content {
|
|
146
|
+
text-align: end;
|
|
147
|
+
}
|
|
148
|
+
/* a little room on the side away from the icon */
|
|
149
|
+
.start:has(> .icon) .content {
|
|
150
|
+
padding-inline-end: var(--space_sm);
|
|
133
151
|
}
|
|
134
|
-
.
|
|
135
|
-
|
|
136
|
-
padding-left: var(--space_sm);
|
|
152
|
+
.end:has(> .icon) .content {
|
|
153
|
+
padding-inline-start: var(--space_sm);
|
|
137
154
|
}
|
|
138
155
|
.icon {
|
|
139
156
|
font-size: var(--icon_size, var(--icon_size_md));
|
|
@@ -141,17 +158,16 @@
|
|
|
141
158
|
display: flex;
|
|
142
159
|
justify-content: center;
|
|
143
160
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
margin-right: var(--icon_margin);
|
|
161
|
+
.start .icon {
|
|
162
|
+
margin-inline-end: var(--icon_margin);
|
|
147
163
|
}
|
|
148
|
-
.
|
|
149
|
-
margin-
|
|
164
|
+
.end .icon {
|
|
165
|
+
margin-inline-start: var(--icon_margin);
|
|
150
166
|
}
|
|
151
|
-
.
|
|
167
|
+
.top .icon {
|
|
152
168
|
margin-bottom: var(--icon_margin);
|
|
153
169
|
}
|
|
154
|
-
.
|
|
170
|
+
.bottom .icon {
|
|
155
171
|
margin-top: var(--icon_margin);
|
|
156
172
|
}
|
|
157
173
|
@media (max-width: 460px) {
|
|
@@ -159,19 +175,12 @@
|
|
|
159
175
|
font-size: var(--font_size_xl);
|
|
160
176
|
}
|
|
161
177
|
}
|
|
178
|
+
/* the icon's size and margin come from the variables, whichever side it is on */
|
|
162
179
|
@media (max-width: 380px) {
|
|
163
180
|
.card {
|
|
164
181
|
--icon_size: var(--icon_size_md);
|
|
165
182
|
--icon_margin: var(--space_sm);
|
|
166
183
|
font-size: var(--font_size_lg);
|
|
167
184
|
}
|
|
168
|
-
.icon {
|
|
169
|
-
font-size: var(--icon_size_md);
|
|
170
|
-
margin-right: var(--space_sm);
|
|
171
|
-
}
|
|
172
|
-
.right :global(.icon) {
|
|
173
|
-
margin-right: 0;
|
|
174
|
-
margin-left: var(--space_sm);
|
|
175
|
-
}
|
|
176
185
|
}
|
|
177
186
|
</style>
|
package/dist/Card.svelte.d.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
3
|
-
type $$ComponentProps = // generic element attrs, the common denominator of the rendered roots
|
|
3
|
+
type $$ComponentProps = // generic element attrs, the common denominator of the rendered roots;
|
|
4
4
|
HTMLAttributes<HTMLElement> & {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Renders the card as an `<a>`, marked `selected` while it leads to the current
|
|
7
|
+
* page, ignoring a trailing slash, the query, and the hash.
|
|
8
|
+
*/
|
|
6
9
|
href?: string | undefined;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
/** The element to render, defaulting to `a` with an `href` and `div` without. */
|
|
11
|
+
tag?: keyof HTMLElementTagNameMap | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* The card's layout: where the icon sits, at the inline start or end beside the
|
|
14
|
+
* content or above or below it, and how the content aligns, toward the end for
|
|
15
|
+
* `end` and centered for `top` and `bottom`.
|
|
16
|
+
* @default 'start'
|
|
17
|
+
*/
|
|
18
|
+
align?: 'start' | 'end' | 'top' | 'bottom';
|
|
19
|
+
/**
|
|
20
|
+
* A decorative icon, hidden from assistive tech: a string like an emoji or a glyph,
|
|
21
|
+
* or a snippet for anything else. None when absent, null, or empty.
|
|
22
|
+
*/
|
|
23
|
+
icon?: string | Snippet | null;
|
|
10
24
|
/** Anchor attributes, applied only when `href` renders the card as an `<a>`. */
|
|
11
25
|
a_attrs?: SvelteHTMLElements['a'];
|
|
12
26
|
children: Snippet;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Card.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Card.svelte"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"Card.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Card.svelte"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAIzE,KAAK,gBAAgB,GAEpB,AAFwB,uEAAuE;AAE/F,cAAc,CAAC,WAAW,CAAC,GAAG;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,qBAAqB,GAAG,SAAS,CAAC;IAC9C;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC/B,gFAAgF;IAChF,OAAO,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAClC,QAAQ,EAAE,OAAO,CAAC;CAClB,CAAC;AAsEJ,QAAA,MAAM,IAAI,sDAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { contextmenu_context } from './contextmenu_state.svelte.js';
|
|
8
8
|
import { icon_external_link, icon_link } from './icons.js';
|
|
9
|
+
import { href_is_current_page } from './page_helpers.js';
|
|
9
10
|
import type { SvgData } from './svg.js';
|
|
10
11
|
import Svg from './Svg.svelte';
|
|
11
12
|
import ContextmenuIcon from './ContextmenuIcon.svelte';
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
const external = $derived(!(text[0] === '.' || (text[0] === '/' && text[1] !== '/')));
|
|
54
55
|
const rel = $derived(external ? external_rel : undefined);
|
|
55
56
|
|
|
56
|
-
const current_page = $derived(page.url
|
|
57
|
+
const current_page = $derived(href_is_current_page(href, page.url));
|
|
57
58
|
</script>
|
|
58
59
|
|
|
59
60
|
<li role="none">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextmenuLinkEntry.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/ContextmenuLinkEntry.svelte"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"ContextmenuLinkEntry.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/ContextmenuLinkEntry.svelte"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAIvC,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AA4FH,QAAA,MAAM,oBAAoB,sDAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
|
package/dist/Docs.svelte
CHANGED
|
@@ -18,9 +18,30 @@
|
|
|
18
18
|
|
|
19
19
|
const {
|
|
20
20
|
tomes,
|
|
21
|
+
primary_nav,
|
|
22
|
+
secondary_nav_header,
|
|
23
|
+
secondary_nav_footer,
|
|
24
|
+
footer,
|
|
21
25
|
children
|
|
22
26
|
}: {
|
|
23
27
|
tomes: Array<Tome>;
|
|
28
|
+
/**
|
|
29
|
+
* Rendered in the primary nav after the breadcrumb, at its end, e.g. links to the
|
|
30
|
+
* site's other sections.
|
|
31
|
+
*/
|
|
32
|
+
primary_nav?: Snippet;
|
|
33
|
+
/**
|
|
34
|
+
* Rendered above the tome menu in the secondary nav, in the sidebar and in the
|
|
35
|
+
* menu dialog of narrow screens, where the primary nav has little room.
|
|
36
|
+
*/
|
|
37
|
+
secondary_nav_header?: Snippet;
|
|
38
|
+
/** Rendered below the tome menu in the secondary nav, in the sidebar and in the menu dialog. */
|
|
39
|
+
secondary_nav_footer?: Snippet;
|
|
40
|
+
/**
|
|
41
|
+
* Replaces the default footer, a `DocsFooter` with the site's repo and a link to
|
|
42
|
+
* fuz.dev, e.g. for a site that isn't part of the Fuz stack.
|
|
43
|
+
*/
|
|
44
|
+
footer?: Snippet;
|
|
24
45
|
children: Snippet;
|
|
25
46
|
} = $props();
|
|
26
47
|
|
|
@@ -52,6 +73,11 @@
|
|
|
52
73
|
|
|
53
74
|
<div class="docs" style:--docs_menu_width={docs_menu_width}>
|
|
54
75
|
<DocsPrimaryNav>
|
|
76
|
+
{#if primary_nav}
|
|
77
|
+
<div class="primary-nav-end">
|
|
78
|
+
{@render primary_nav()}
|
|
79
|
+
</div>
|
|
80
|
+
{/if}
|
|
55
81
|
<div class="nav-dialog-toggle">
|
|
56
82
|
<button class="plain" type="button" onclick={() => toggle_secondary_nav_dialog()}>
|
|
57
83
|
menu
|
|
@@ -61,7 +87,7 @@
|
|
|
61
87
|
<!-- TODO @many dialog navs -->
|
|
62
88
|
{#if !innerWidth.current || innerWidth.current > SECONDARY_NAV_BREAKPOINT}
|
|
63
89
|
<div class="secondary-nav-wrapper">
|
|
64
|
-
<DocsSecondaryNav {tomes} />
|
|
90
|
+
<DocsSecondaryNav {tomes} header={secondary_nav_header} footer={secondary_nav_footer} />
|
|
65
91
|
</div>
|
|
66
92
|
{/if}
|
|
67
93
|
<main>
|
|
@@ -73,11 +99,15 @@
|
|
|
73
99
|
<DocsTertiaryNav {tomes} {tomes_by_slug} />
|
|
74
100
|
{/if}
|
|
75
101
|
<section class="box">
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
102
|
+
{#if footer}
|
|
103
|
+
{@render footer()}
|
|
104
|
+
{:else}
|
|
105
|
+
<DocsFooter repo_url={site.repo_url} root_url={FUZ_DEV_URL}>
|
|
106
|
+
<div class="mb_xl5">
|
|
107
|
+
<Breadcrumb />
|
|
108
|
+
</div>
|
|
109
|
+
</DocsFooter>
|
|
110
|
+
{/if}
|
|
81
111
|
</section>
|
|
82
112
|
</main>
|
|
83
113
|
</div>
|
|
@@ -94,7 +124,12 @@
|
|
|
94
124
|
<Breadcrumb />
|
|
95
125
|
</div>
|
|
96
126
|
<div class="px_lg pb_xl">
|
|
97
|
-
<DocsSecondaryNav
|
|
127
|
+
<DocsSecondaryNav
|
|
128
|
+
{tomes}
|
|
129
|
+
sidebar={false}
|
|
130
|
+
header={secondary_nav_header}
|
|
131
|
+
footer={secondary_nav_footer}
|
|
132
|
+
/>
|
|
98
133
|
<DocsTertiaryNav {tomes} {tomes_by_slug} sidebar={false} />
|
|
99
134
|
</div>
|
|
100
135
|
</div>
|
|
@@ -131,6 +166,12 @@
|
|
|
131
166
|
display: contents;
|
|
132
167
|
}
|
|
133
168
|
|
|
169
|
+
.primary-nav-end {
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
gap: var(--space_md);
|
|
173
|
+
padding: 0 var(--space_md);
|
|
174
|
+
}
|
|
134
175
|
.nav-dialog-toggle {
|
|
135
176
|
display: none;
|
|
136
177
|
}
|
package/dist/Docs.svelte.d.ts
CHANGED
|
@@ -2,6 +2,23 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
import { Tome } from './tome.js';
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
tomes: Array<Tome>;
|
|
5
|
+
/**
|
|
6
|
+
* Rendered in the primary nav after the breadcrumb, at its end, e.g. links to the
|
|
7
|
+
* site's other sections.
|
|
8
|
+
*/
|
|
9
|
+
primary_nav?: Snippet;
|
|
10
|
+
/**
|
|
11
|
+
* Rendered above the tome menu in the secondary nav, in the sidebar and in the
|
|
12
|
+
* menu dialog of narrow screens, where the primary nav has little room.
|
|
13
|
+
*/
|
|
14
|
+
secondary_nav_header?: Snippet;
|
|
15
|
+
/** Rendered below the tome menu in the secondary nav, in the sidebar and in the menu dialog. */
|
|
16
|
+
secondary_nav_footer?: Snippet;
|
|
17
|
+
/**
|
|
18
|
+
* Replaces the default footer, a `DocsFooter` with the site's repo and a link to
|
|
19
|
+
* fuz.dev, e.g. for a site that isn't part of the Fuz stack.
|
|
20
|
+
*/
|
|
21
|
+
footer?: Snippet;
|
|
5
22
|
children: Snippet;
|
|
6
23
|
};
|
|
7
24
|
declare const Docs: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Docs.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Docs.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,EAAE,IAAI,EAAiB,MAAM,WAAW,CAAC;AAW/C,KAAK,gBAAgB,GAAI;IACxB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CAClB,CAAC;
|
|
1
|
+
{"version":3,"file":"Docs.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/Docs.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,EAAE,IAAI,EAAiB,MAAM,WAAW,CAAC;AAW/C,KAAK,gBAAgB,GAAI;IACxB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gGAAgG;IAChG,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CAClB,CAAC;AAsHH,QAAA,MAAM,IAAI,sDAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { SvelteHTMLElements } from 'svelte/elements';
|
|
3
4
|
|
|
4
5
|
import DocsMenu from './DocsMenu.svelte';
|
|
@@ -7,9 +8,15 @@
|
|
|
7
8
|
const {
|
|
8
9
|
tomes,
|
|
9
10
|
sidebar = true,
|
|
11
|
+
header,
|
|
12
|
+
footer,
|
|
10
13
|
...rest
|
|
11
14
|
}: SvelteHTMLElements['aside'] & {
|
|
12
15
|
tomes: Array<Tome>;
|
|
16
|
+
/** Rendered above the menu, e.g. links to the site's other sections. */
|
|
17
|
+
header?: Snippet;
|
|
18
|
+
/** Rendered below the menu. */
|
|
19
|
+
footer?: Snippet;
|
|
13
20
|
sidebar?: boolean; // TODO @many dialog navs (this shouldn't exist)
|
|
14
21
|
} = $props();
|
|
15
22
|
|
|
@@ -18,7 +25,9 @@
|
|
|
18
25
|
|
|
19
26
|
<aside {...rest} class={['docs-secondary-nav unstyled', rest.class, { sidebar }]}>
|
|
20
27
|
<nav aria-label="Secondary nav">
|
|
28
|
+
{@render header?.()}
|
|
21
29
|
<DocsMenu {tomes} {expand_width} />
|
|
30
|
+
{@render footer?.()}
|
|
22
31
|
</nav>
|
|
23
32
|
</aside>
|
|
24
33
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { SvelteHTMLElements } from 'svelte/elements';
|
|
2
3
|
import { Tome } from './tome.js';
|
|
3
4
|
type $$ComponentProps = SvelteHTMLElements['aside'] & {
|
|
4
5
|
tomes: Array<Tome>;
|
|
6
|
+
/** Rendered above the menu, e.g. links to the site's other sections. */
|
|
7
|
+
header?: Snippet;
|
|
8
|
+
/** Rendered below the menu. */
|
|
9
|
+
footer?: Snippet;
|
|
5
10
|
sidebar?: boolean;
|
|
6
11
|
};
|
|
7
12
|
declare const DocsSecondaryNav: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsSecondaryNav.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/DocsSecondaryNav.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEhC,KAAK,gBAAgB,GAAI,kBAAkB,CAAC,OAAO,CAAC,GAAG;IACtD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;
|
|
1
|
+
{"version":3,"file":"DocsSecondaryNav.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/DocsSecondaryNav.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEhC,KAAK,gBAAgB,GAAI,kBAAkB,CAAC,OAAO,CAAC,GAAG;IACtD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AA+BH,QAAA,MAAM,gBAAgB,sDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
package/dist/alert.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type AlertStatus = 'inform' | 'help' | 'error';
|
|
2
2
|
export interface AlertStatusOptions {
|
|
3
3
|
color: string;
|
|
4
|
-
icon: string
|
|
4
|
+
icon: string;
|
|
5
5
|
}
|
|
6
6
|
export declare const alert_status_options: Record<AlertStatus, AlertStatusOptions>;
|
|
7
7
|
//# sourceMappingURL=alert.d.ts.map
|
package/dist/alert.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/alert.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"alert.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/alert.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,WAAW,EAAE,kBAAkB,CAIxE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for relating links to the current page.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Whether `href` leads to the page at `url`: the same origin and pathname, with a
|
|
8
|
+
* trailing slash, the query, and the hash ignored. A relative `href` resolves
|
|
9
|
+
* against `url`, as a browser resolves it.
|
|
10
|
+
*
|
|
11
|
+
* @param href - a link's `href`, absolute or relative
|
|
12
|
+
* @param url - the current page's url, e.g. `page.url`
|
|
13
|
+
* @returns `false` for an `href` that isn't a valid url
|
|
14
|
+
*/
|
|
15
|
+
export declare const href_is_current_page: (href: string, url: URL) => boolean;
|
|
16
|
+
//# sourceMappingURL=page_helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/page_helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,KAAK,GAAG,KAAG,OAO7D,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for relating links to the current page.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** A pathname without its trailing slash, except the root's. */
|
|
7
|
+
const strip_trailing_slash = (pathname) => pathname.length > 1 && pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
|
|
8
|
+
/**
|
|
9
|
+
* Whether `href` leads to the page at `url`: the same origin and pathname, with a
|
|
10
|
+
* trailing slash, the query, and the hash ignored. A relative `href` resolves
|
|
11
|
+
* against `url`, as a browser resolves it.
|
|
12
|
+
*
|
|
13
|
+
* @param href - a link's `href`, absolute or relative
|
|
14
|
+
* @param url - the current page's url, e.g. `page.url`
|
|
15
|
+
* @returns `false` for an `href` that isn't a valid url
|
|
16
|
+
*/
|
|
17
|
+
export const href_is_current_page = (href, url) => {
|
|
18
|
+
if (!URL.canParse(href, url))
|
|
19
|
+
return false;
|
|
20
|
+
const target = new URL(href, url);
|
|
21
|
+
return (target.origin === url.origin &&
|
|
22
|
+
strip_trailing_slash(target.pathname) === strip_trailing_slash(url.pathname));
|
|
23
|
+
};
|
package/package.json
CHANGED
package/src/lib/alert.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for relating links to the current page.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** A pathname without its trailing slash, except the root's. */
|
|
8
|
+
const strip_trailing_slash = (pathname: string): string =>
|
|
9
|
+
pathname.length > 1 && pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether `href` leads to the page at `url`: the same origin and pathname, with a
|
|
13
|
+
* trailing slash, the query, and the hash ignored. A relative `href` resolves
|
|
14
|
+
* against `url`, as a browser resolves it.
|
|
15
|
+
*
|
|
16
|
+
* @param href - a link's `href`, absolute or relative
|
|
17
|
+
* @param url - the current page's url, e.g. `page.url`
|
|
18
|
+
* @returns `false` for an `href` that isn't a valid url
|
|
19
|
+
*/
|
|
20
|
+
export const href_is_current_page = (href: string, url: URL): boolean => {
|
|
21
|
+
if (!URL.canParse(href, url)) return false;
|
|
22
|
+
const target = new URL(href, url);
|
|
23
|
+
return (
|
|
24
|
+
target.origin === url.origin &&
|
|
25
|
+
strip_trailing_slash(target.pathname) === strip_trailing_slash(url.pathname)
|
|
26
|
+
);
|
|
27
|
+
};
|