akanjs 3.1.0-rc.7 → 3.1.0-rc.8
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/client/href.ts +18 -7
- package/package.json +1 -1
- package/types/client/href.d.ts +3 -3
- package/ui/Clipboard.tsx +1 -0
- package/ui/Data/Item.tsx +3 -0
- package/ui/DraggableList.tsx +1 -0
- package/ui/Field/Collection.tsx +4 -0
- package/ui/Input.tsx +1 -0
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Link/Anchor.tsx +1 -1
- package/ui/Model/Edit.tsx +1 -1
- package/ui/Model/New.tsx +1 -1
- package/ui/Model/SureToRemove.tsx +1 -0
- package/ui/Model/ViewEditModal.tsx +7 -1
- package/ui/ObjectId.tsx +5 -1
- package/ui/Signal/Doc.tsx +5 -1
- package/ui/ToggleSelect.tsx +2 -0
package/client/href.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
export class Href {
|
|
2
2
|
static readonly #blocked = new Set(["javascript:", "vbscript:", "data:"]);
|
|
3
3
|
|
|
4
|
+
static #parse(href: string, base?: string): URL | null {
|
|
5
|
+
try {
|
|
6
|
+
return new URL(href, base);
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
/** External is what the browser takes off-site: any absolute URL with a scheme, and `//`/`\\` network-path
|
|
5
|
-
* forms
|
|
13
|
+
* forms that name a host. javascript:, vbscript: and data: never count. */
|
|
6
14
|
static isExternal(href: string): boolean {
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
const absolute = Href.#parse(href);
|
|
17
|
+
if (absolute) return !Href.#blocked.has(absolute.protocol);
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
const value = href.replace(/[\t\n\r]/g, "").replace(/^[\u0000-\u0020]+/, "");
|
|
20
|
+
return /^[\\/]{2}/.test(value) && !!Href.#parse(value, "https://localhost")?.hostname;
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
/** An explicit http(s) scheme in any case; `//host` and `httpbin/x` do not qualify. */
|
|
@@ -15,10 +25,11 @@ export class Href {
|
|
|
15
25
|
return /^https?:/i.test(href);
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
/** Canonical absolute URL for an external href:
|
|
19
|
-
*
|
|
28
|
+
/** Canonical absolute URL for an external href: follows the browser on an http(s) document base; on a native
|
|
29
|
+
* `app://` base an absolute URL resolves as written and a network path resolves against https:. */
|
|
20
30
|
static toExternalUrl(href: string, base: string): string {
|
|
21
|
-
const
|
|
22
|
-
|
|
31
|
+
const baseUrl = Href.#parse(base);
|
|
32
|
+
if (baseUrl && /^https?:$/.test(baseUrl.protocol)) return Href.#parse(href, baseUrl.href)?.href ?? href;
|
|
33
|
+
return (Href.#parse(href) ?? Href.#parse(href, "https://localhost"))?.href ?? href;
|
|
23
34
|
}
|
|
24
35
|
}
|
package/package.json
CHANGED
package/types/client/href.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export declare class Href {
|
|
2
2
|
#private;
|
|
3
3
|
/** External is what the browser takes off-site: any absolute URL with a scheme, and `//`/`\\` network-path
|
|
4
|
-
* forms
|
|
4
|
+
* forms that name a host. javascript:, vbscript: and data: never count. */
|
|
5
5
|
static isExternal(href: string): boolean;
|
|
6
6
|
/** An explicit http(s) scheme in any case; `//host` and `httpbin/x` do not qualify. */
|
|
7
7
|
static hasWebScheme(href: string): boolean;
|
|
8
|
-
/** Canonical absolute URL for an external href:
|
|
9
|
-
*
|
|
8
|
+
/** Canonical absolute URL for an external href: follows the browser on an http(s) document base; on a native
|
|
9
|
+
* `app://` base an absolute URL resolves as written and a network path resolves against https:. */
|
|
10
10
|
static toExternalUrl(href: string, base: string): string;
|
|
11
11
|
}
|
package/ui/Clipboard.tsx
CHANGED
package/ui/Data/Item.tsx
CHANGED
|
@@ -194,6 +194,7 @@ export const Action = <T extends string, M extends { id: string }, L extends { i
|
|
|
194
194
|
};
|
|
195
195
|
return action === "edit" ? (
|
|
196
196
|
<button
|
|
197
|
+
type="button"
|
|
197
198
|
className={buttonRecipe({ variant: outline ? "outline" : "ghost", size: "icon" }, [
|
|
198
199
|
"m-1 size-8 text-center",
|
|
199
200
|
outline && "border-dashed",
|
|
@@ -204,6 +205,7 @@ export const Action = <T extends string, M extends { id: string }, L extends { i
|
|
|
204
205
|
</button>
|
|
205
206
|
) : action === "view" ? (
|
|
206
207
|
<button
|
|
208
|
+
type="button"
|
|
207
209
|
className={buttonRecipe({ variant: outline ? "outline" : "ghost", size: "icon" }, [
|
|
208
210
|
"m-1 size-8 text-center",
|
|
209
211
|
outline && "border-dashed",
|
|
@@ -219,6 +221,7 @@ export const Action = <T extends string, M extends { id: string }, L extends { i
|
|
|
219
221
|
onConfirm={() => void storeDo[namesOfSlice.removeModel](model.id)}
|
|
220
222
|
>
|
|
221
223
|
<button
|
|
224
|
+
type="button"
|
|
222
225
|
className={buttonRecipe({ variant: outline ? "outline" : "ghost", size: "icon" }, [
|
|
223
226
|
"m-1 size-8 text-center",
|
|
224
227
|
outline && "border-dashed",
|
package/ui/DraggableList.tsx
CHANGED
|
@@ -183,6 +183,7 @@ const Item = ({
|
|
|
183
183
|
{children}
|
|
184
184
|
{removable && (
|
|
185
185
|
<button
|
|
186
|
+
type="button"
|
|
186
187
|
className={recipe({ variant: "outline", size: "xs" }, [
|
|
187
188
|
"size-6 border-destructive p-0 text-destructive hover:bg-destructive hover:text-destructive-foreground",
|
|
188
189
|
removeClassName,
|
package/ui/Field/Collection.tsx
CHANGED
|
@@ -50,6 +50,7 @@ export const List = <Item,>({
|
|
|
50
50
|
{renderItem(item, idx)}
|
|
51
51
|
<div className="flex gap-2 border-border border-l pl-2">
|
|
52
52
|
<button
|
|
53
|
+
type="button"
|
|
53
54
|
className={recipe(
|
|
54
55
|
{ variant: "outline", size: "icon" },
|
|
55
56
|
"size-6 border-destructive p-0 text-destructive hover:bg-destructive hover:text-destructive-foreground",
|
|
@@ -66,6 +67,7 @@ export const List = <Item,>({
|
|
|
66
67
|
</Fragment>
|
|
67
68
|
))}
|
|
68
69
|
<button
|
|
70
|
+
type="button"
|
|
69
71
|
className={recipe({ variant: "outline" })}
|
|
70
72
|
onClick={() => {
|
|
71
73
|
onAdd();
|
|
@@ -151,6 +153,7 @@ export const TextList = ({
|
|
|
151
153
|
disabled={disabled}
|
|
152
154
|
/>
|
|
153
155
|
<button
|
|
156
|
+
type="button"
|
|
154
157
|
className={recipe(
|
|
155
158
|
{ variant: "outline", size: "icon" },
|
|
156
159
|
"size-6 border-destructive p-0 text-destructive hover:bg-destructive hover:text-destructive-foreground",
|
|
@@ -169,6 +172,7 @@ export const TextList = ({
|
|
|
169
172
|
<div className="my-5 h-[0.5px] bg-foreground/20" />
|
|
170
173
|
{texts.length < maxlength ? (
|
|
171
174
|
<button
|
|
175
|
+
type="button"
|
|
172
176
|
className={recipe({ variant: "outline" }, "w-full")}
|
|
173
177
|
onClick={() => {
|
|
174
178
|
onChange([...texts, ""]);
|
package/ui/Input.tsx
CHANGED
package/ui/Layout/Sider.tsx
CHANGED
|
@@ -127,7 +127,7 @@ export const LeftSider = ({ className, children, open, width, close, onCancel }:
|
|
|
127
127
|
}}
|
|
128
128
|
>
|
|
129
129
|
{close ?? (
|
|
130
|
-
<button className={buttonRecipe({ variant: "ghost", size: "icon" })}>
|
|
130
|
+
<button type="button" className={buttonRecipe({ variant: "ghost", size: "icon" })}>
|
|
131
131
|
<BiX />
|
|
132
132
|
</button>
|
|
133
133
|
)}
|
package/ui/Link/Anchor.tsx
CHANGED
|
@@ -53,7 +53,7 @@ export const CsrLink = ({
|
|
|
53
53
|
const isExternal = Href.isExternal(href);
|
|
54
54
|
const url = href.startsWith("#") ? `${csrLocation.pathname}#${hash}` : href;
|
|
55
55
|
if (isExternal) {
|
|
56
|
-
const target = Href.toExternalUrl(href,
|
|
56
|
+
const target = Href.toExternalUrl(href, document.baseURI);
|
|
57
57
|
void openExternalUrl(target).catch(() => window.open(target, "_blank", "noopener,noreferrer"));
|
|
58
58
|
} else if (replace) router.replace(url, { scrollToTop });
|
|
59
59
|
else router.push(url, { scrollToTop });
|
package/ui/Model/Edit.tsx
CHANGED
|
@@ -24,7 +24,7 @@ export default function Edit({ trigger, children, slice, modelId, modal, renderT
|
|
|
24
24
|
<>
|
|
25
25
|
<EditWrapper className="contents" slice={slice} modelId={modelId} modal={modal} draft={draft}>
|
|
26
26
|
{trigger ?? (
|
|
27
|
-
<button className={buttonRecipe({ variant: "primary" })}>
|
|
27
|
+
<button type="button" className={buttonRecipe({ variant: "primary" })}>
|
|
28
28
|
<AiOutlineEdit /> {l("base.edit")}
|
|
29
29
|
</button>
|
|
30
30
|
)}
|
package/ui/Model/New.tsx
CHANGED
|
@@ -45,7 +45,7 @@ export default function New({
|
|
|
45
45
|
draft={draft}
|
|
46
46
|
>
|
|
47
47
|
{trigger ?? (
|
|
48
|
-
<button className={buttonRecipe({ variant: "primary" })}>
|
|
48
|
+
<button type="button" className={buttonRecipe({ variant: "primary" })}>
|
|
49
49
|
<AiOutlinePlus /> {l("base.new")}
|
|
50
50
|
</button>
|
|
51
51
|
)}
|
|
@@ -89,6 +89,7 @@ export default function SureToRemove({
|
|
|
89
89
|
bodyClassName="border-destructive"
|
|
90
90
|
action={
|
|
91
91
|
<button
|
|
92
|
+
type="button"
|
|
92
93
|
className={buttonRecipe({ variant: "destructive" }, "w-full")}
|
|
93
94
|
disabled={typeNameToRemove && repeatName !== name}
|
|
94
95
|
onClick={async () => {
|
|
@@ -45,7 +45,12 @@ const EditAction = ({ modelName, label, onAct, closeView }: ActionProps) => {
|
|
|
45
45
|
.desc(`Turn the open ${modelName} from its detail view into the edit form.`)
|
|
46
46
|
.exec(onAct);
|
|
47
47
|
return (
|
|
48
|
-
<button
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
className={buttonRecipe({ variant: "primary" }, "w-full")}
|
|
51
|
+
onClick={editModel}
|
|
52
|
+
{...agentAttrs(editModel)}
|
|
53
|
+
>
|
|
49
54
|
<AiOutlineEdit /> {label}
|
|
50
55
|
</button>
|
|
51
56
|
);
|
|
@@ -59,6 +64,7 @@ const SaveAction = ({ modelName, label, onAct, closeView }: ActionProps) => {
|
|
|
59
64
|
.exec(onAct);
|
|
60
65
|
return (
|
|
61
66
|
<button
|
|
67
|
+
type="button"
|
|
62
68
|
className={buttonRecipe({ variant: "primary" }, "w-full")}
|
|
63
69
|
onClick={submitModel}
|
|
64
70
|
{...agentAttrs(submitModel)}
|
package/ui/ObjectId.tsx
CHANGED
|
@@ -26,7 +26,11 @@ export const ObjectId = ({ id }: ObjectIdProps) => {
|
|
|
26
26
|
<Tooltip content={id} variant="primary">
|
|
27
27
|
<div className="font-semibold text-xs">{shortenedId}</div>
|
|
28
28
|
</Tooltip>
|
|
29
|
-
<button
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
className={recipe({ variant: "ghost", size: "icon" }, "size-6 rounded-full")}
|
|
32
|
+
onClick={handleCopyClick}
|
|
33
|
+
>
|
|
30
34
|
{isCopied ? <AiOutlineCheck /> : <AiOutlineCopy />}
|
|
31
35
|
</button>
|
|
32
36
|
</div>
|
package/ui/Signal/Doc.tsx
CHANGED
|
@@ -74,7 +74,10 @@ export const DocSetting = ({ fetch = runtimeFetch, search, onSearch }: DocSettin
|
|
|
74
74
|
<Toolbar>
|
|
75
75
|
<ToolbarField label={l.trans(signalText.baseUrl)}>
|
|
76
76
|
<Copy text={fetch.origin}>
|
|
77
|
-
<button
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
className={buttonRecipe({ variant: "ghost", size: "sm" }, "font-mono text-foreground/80")}
|
|
80
|
+
>
|
|
78
81
|
{fetch.origin}
|
|
79
82
|
<AiOutlineCopy className="text-foreground/40" />
|
|
80
83
|
</button>
|
|
@@ -173,6 +176,7 @@ export const DocAuthModal = ({ children }: DocAuthModalProps) => {
|
|
|
173
176
|
title={l.trans(signalText.setJwtTitle)}
|
|
174
177
|
action={
|
|
175
178
|
<button
|
|
179
|
+
type="button"
|
|
176
180
|
className={buttonRecipe({ variant: "primary" }, "w-full")}
|
|
177
181
|
onClick={() => {
|
|
178
182
|
st.set(
|
package/ui/ToggleSelect.tsx
CHANGED
|
@@ -70,6 +70,7 @@ const DefaultToggleSelect = <I extends string | number | boolean | null>({
|
|
|
70
70
|
<Fragment key={idx}>{renderItem(option, { selected: isSelected, disabled: isDisabled, onToggle })}</Fragment>
|
|
71
71
|
) : (
|
|
72
72
|
<button
|
|
73
|
+
type="button"
|
|
73
74
|
key={idx}
|
|
74
75
|
disabled={isDisabled}
|
|
75
76
|
className={cn(toggleBtn, isSelected && selectedCls, isDisabled && "cursor-not-allowed", btnClassName)}
|
|
@@ -128,6 +129,7 @@ const DefaultMulti = ({
|
|
|
128
129
|
const isDisabled = (disabled ?? false) || (option.disabled ?? false);
|
|
129
130
|
return (
|
|
130
131
|
<button
|
|
132
|
+
type="button"
|
|
131
133
|
key={idx}
|
|
132
134
|
disabled={isDisabled}
|
|
133
135
|
className={cn(toggleBtn, isSelected && selectedCls, isDisabled && "cursor-not-allowed", btnClassName)}
|