@celestia-island/hikari 0.45.2 → 0.45.4
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/package.json +1 -1
- package/src/components/HkAboutModal.scss +31 -3
- package/src/components/HkAboutModal.test.tsx +30 -16
- package/src/components/HkAboutModal.tsx +41 -17
- package/src/components/HkModal.footerLead.test.tsx +123 -0
- package/src/components/HkModal.scss +15 -0
- package/src/components/HkModal.tsx +6 -3
- package/src/i18n/locales/ar/platform.json +0 -1
- package/src/i18n/locales/de/platform.json +0 -1
- package/src/i18n/locales/en/platform.json +0 -1
- package/src/i18n/locales/es/platform.json +0 -1
- package/src/i18n/locales/fr/platform.json +0 -1
- package/src/i18n/locales/ja/platform.json +0 -1
- package/src/i18n/locales/ko/platform.json +0 -1
- package/src/i18n/locales/pt/platform.json +0 -1
- package/src/i18n/locales/ru/platform.json +0 -1
- package/src/i18n/locales/zh-Hans/platform.json +0 -1
- package/src/i18n/locales/zh-Hant/platform.json +0 -1
package/package.json
CHANGED
|
@@ -72,6 +72,14 @@
|
|
|
72
72
|
color: rgb(var(--color-muted));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
.s-about-modal-slogan {
|
|
76
|
+
margin: 0;
|
|
77
|
+
font-size: var(--text-base, 1rem);
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
text-align: center;
|
|
80
|
+
color: rgb(var(--color-text));
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
.s-about-modal-tagline {
|
|
76
84
|
margin: var(--space-2, 0.125rem) 0 0;
|
|
77
85
|
font-size: var(--text-xs, 0.75rem);
|
|
@@ -80,8 +88,9 @@
|
|
|
80
88
|
|
|
81
89
|
.s-about-modal-description {
|
|
82
90
|
margin: 0;
|
|
83
|
-
font-size: var(--text-
|
|
84
|
-
line-height: 1.
|
|
91
|
+
font-size: var(--text-xs, 0.75rem);
|
|
92
|
+
line-height: 1.6;
|
|
93
|
+
text-align: center;
|
|
85
94
|
color: rgb(var(--color-muted));
|
|
86
95
|
}
|
|
87
96
|
|
|
@@ -127,6 +136,7 @@
|
|
|
127
136
|
.s-about-modal-links-list {
|
|
128
137
|
display: flex;
|
|
129
138
|
flex-wrap: wrap;
|
|
139
|
+
justify-content: center;
|
|
130
140
|
gap: var(--space-8, 0.5rem);
|
|
131
141
|
}
|
|
132
142
|
|
|
@@ -154,7 +164,25 @@
|
|
|
154
164
|
0 0 0 4px rgb(var(--color-primary));
|
|
155
165
|
}
|
|
156
166
|
|
|
167
|
+
.s-about-modal-footer-links {
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-wrap: wrap;
|
|
170
|
+
justify-content: center;
|
|
171
|
+
gap: var(--space-12, 0.75rem);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.s-about-modal-footer-link {
|
|
175
|
+
font-size: var(--text-xs, 0.75rem);
|
|
176
|
+
color: rgb(var(--color-muted));
|
|
177
|
+
text-decoration: none;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.s-about-modal-footer-link:hover {
|
|
181
|
+
color: rgb(var(--color-primary));
|
|
182
|
+
text-decoration: underline;
|
|
183
|
+
}
|
|
184
|
+
|
|
157
185
|
.s-about-modal-footer {
|
|
158
186
|
display: flex;
|
|
159
|
-
justify-content:
|
|
187
|
+
justify-content: center;
|
|
160
188
|
}
|
|
@@ -94,15 +94,18 @@ describe("HkAboutModal branding", () => {
|
|
|
94
94
|
expect(query<HTMLElement>(".s-about-modal-description").textContent).toBe("Save the world.");
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
it("renders
|
|
97
|
+
it("renders the made-by row with a linked origin", async () => {
|
|
98
98
|
mountAbout({
|
|
99
|
-
author: "
|
|
100
|
-
|
|
99
|
+
author: "由 伊欧 主创",
|
|
100
|
+
origin: "来自 Celestia Island",
|
|
101
|
+
originHref: "https://github.com/celestia-island",
|
|
101
102
|
license: "BUSL-1.1",
|
|
102
103
|
});
|
|
103
104
|
await flushModal();
|
|
105
|
+
const row = query<HTMLElement>(".s-about-modal-row");
|
|
106
|
+
expect(row.querySelector(".s-about-modal-row-label")?.textContent).toBe("由 伊欧 主创");
|
|
104
107
|
const link = query<HTMLAnchorElement>(".s-about-modal-row-link");
|
|
105
|
-
expect(link.textContent).toBe("Celestia Island");
|
|
108
|
+
expect(link.textContent).toBe("来自 Celestia Island");
|
|
106
109
|
expect(link.getAttribute("href")).toBe("https://github.com/celestia-island");
|
|
107
110
|
expect(link.getAttribute("target")).toBe("_blank");
|
|
108
111
|
const rows = [...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row")];
|
|
@@ -111,13 +114,28 @@ describe("HkAboutModal branding", () => {
|
|
|
111
114
|
expect(licenseRow!.textContent).toContain("License");
|
|
112
115
|
});
|
|
113
116
|
|
|
114
|
-
it("renders the
|
|
115
|
-
mountAbout({ author: "Celestia Island" });
|
|
117
|
+
it("renders the made-by row as plain text without originHref", async () => {
|
|
118
|
+
mountAbout({ author: "由 伊欧 主创", origin: "来自 Celestia Island" });
|
|
116
119
|
await flushModal();
|
|
117
|
-
expect(query<HTMLElement>(".s-about-modal-row").textContent).toContain("Celestia Island");
|
|
120
|
+
expect(query<HTMLElement>(".s-about-modal-row").textContent).toContain("来自 Celestia Island");
|
|
118
121
|
expect(document.body.querySelector(".s-about-modal-row-link")).toBeNull();
|
|
119
122
|
});
|
|
120
123
|
|
|
124
|
+
it("renders the centered slogan and centered legal footer links", async () => {
|
|
125
|
+
mountAbout({
|
|
126
|
+
slogan: "技术宅拯救世界。",
|
|
127
|
+
footerLinks: [{ label: "京ICP备2026xxxx号", href: "https://beian.miit.gov.cn/" }],
|
|
128
|
+
});
|
|
129
|
+
await flushModal();
|
|
130
|
+
expect(query<HTMLElement>(".s-about-modal-slogan").textContent).toBe("技术宅拯救世界。");
|
|
131
|
+
const links = [
|
|
132
|
+
...document.body.querySelectorAll<HTMLAnchorElement>(".s-about-modal-footer-link"),
|
|
133
|
+
];
|
|
134
|
+
expect(links.length).toBe(1);
|
|
135
|
+
expect(links[0]!.textContent).toBe("京ICP备2026xxxx号");
|
|
136
|
+
expect(links[0]!.getAttribute("href")).toBe("https://beian.miit.gov.cn/");
|
|
137
|
+
});
|
|
138
|
+
|
|
121
139
|
it("renders the backdrop factory inside the clipped layer", async () => {
|
|
122
140
|
mountAbout({ backdrop: () => h("div", { class: "test-backdrop-marker" }) });
|
|
123
141
|
await flushModal();
|
|
@@ -159,23 +177,19 @@ describe("HkAboutModal branding", () => {
|
|
|
159
177
|
expect(badge.textContent).toContain(`© ${year} Test App`);
|
|
160
178
|
});
|
|
161
179
|
|
|
162
|
-
it("localizes the
|
|
163
|
-
mountAbout({
|
|
180
|
+
it("localizes the license label", async () => {
|
|
181
|
+
mountAbout({ license: "BUSL-1.1" });
|
|
164
182
|
await flushModal();
|
|
165
|
-
const
|
|
183
|
+
const labels = () => [
|
|
166
184
|
...document.body.querySelectorAll<HTMLElement>(".s-about-modal-row-label"),
|
|
167
185
|
];
|
|
168
186
|
|
|
169
187
|
setLocale("zh-Hans");
|
|
170
188
|
await flushModal();
|
|
171
|
-
|
|
172
|
-
expect(text.some((t) => t === "作者")).toBe(true);
|
|
173
|
-
expect(text.some((t) => t === "许可证")).toBe(true);
|
|
189
|
+
expect(labels().some((el) => el.textContent === "许可证")).toBe(true);
|
|
174
190
|
|
|
175
191
|
setLocale("en");
|
|
176
192
|
await flushModal();
|
|
177
|
-
|
|
178
|
-
expect(text.some((t) => t === "Author")).toBe(true);
|
|
179
|
-
expect(text.some((t) => t === "License")).toBe(true);
|
|
193
|
+
expect(labels().some((el) => el.textContent === "License")).toBe(true);
|
|
180
194
|
});
|
|
181
195
|
});
|
|
@@ -15,12 +15,13 @@ export interface HAboutLink {
|
|
|
15
15
|
* (Upstreamed from shittim-chest's plana-legacy layer.)
|
|
16
16
|
*
|
|
17
17
|
* Shows the app identity and version metadata plus optional branding: a
|
|
18
|
-
* logo image, tagline,
|
|
19
|
-
* decorative backdrop layer (canvas or anything
|
|
20
|
-
* content and pointer-inert)
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* logo image, tagline, centered slogan + organization line, a made-by /
|
|
19
|
+
* origin row, license row, a decorative backdrop layer (canvas or anything
|
|
20
|
+
* else, rendered behind the content and pointer-inert), external link
|
|
21
|
+
* chips and centered legal footer links (e.g. ICP filings). Version/build
|
|
22
|
+
* hashes render as short hashes when longer than 12 chars (full value in
|
|
23
|
+
* `title` tooltip). Every branding prop is optional — the modal degrades
|
|
24
|
+
* to the plain identity + version card when none are given.
|
|
24
25
|
*/
|
|
25
26
|
export const HkAboutModal = defineComponent({
|
|
26
27
|
name: "HkAboutModal",
|
|
@@ -40,18 +41,24 @@ export const HkAboutModal = defineComponent({
|
|
|
40
41
|
logoSrc: { type: String, default: undefined },
|
|
41
42
|
/** Optional one-liner under the version (e.g. the app tagline). */
|
|
42
43
|
tagline: { type: String, default: undefined },
|
|
43
|
-
/** Optional
|
|
44
|
+
/** Optional centered slogan below the header (the app's one-liner). */
|
|
45
|
+
slogan: { type: String, default: undefined },
|
|
46
|
+
/** Optional centered small line under the slogan (organization blurb). */
|
|
44
47
|
description: { type: String, default: undefined },
|
|
45
|
-
/** Optional
|
|
48
|
+
/** Optional made-by row: left label (e.g. "由 伊欧 主创"). */
|
|
46
49
|
author: { type: String, default: undefined },
|
|
47
|
-
/** Optional
|
|
48
|
-
|
|
50
|
+
/** Optional right value of the made-by row (e.g. "来自 Celestia Island"). */
|
|
51
|
+
origin: { type: String, default: undefined },
|
|
52
|
+
/** Optional link applied to the origin value. */
|
|
53
|
+
originHref: { type: String, default: undefined },
|
|
49
54
|
/** Optional license identifier row (e.g. "BUSL-1.1"). */
|
|
50
55
|
license: { type: String, default: undefined },
|
|
51
56
|
/** Optional copyright holder in the footer (defaults to the app name). */
|
|
52
57
|
copyright: { type: String, default: undefined },
|
|
53
58
|
/** Optional external links (e.g. GitHub, docs). */
|
|
54
59
|
links: { type: Array as PropType<HAboutLink[]>, default: () => [] },
|
|
60
|
+
/** Optional centered legal links above the copyright (ICP filings). */
|
|
61
|
+
footerLinks: { type: Array as PropType<HAboutLink[]>, default: () => [] },
|
|
55
62
|
/**
|
|
56
63
|
* Optional decorative backdrop factory, rendered behind the content
|
|
57
64
|
* inside a clipped, pointer-inert layer. The modal owns only the layer —
|
|
@@ -70,16 +77,16 @@ export const HkAboutModal = defineComponent({
|
|
|
70
77
|
return hash.length > 12 ? `${hash.slice(0, 12)}…` : hash;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
const
|
|
74
|
-
if (!props.
|
|
80
|
+
const renderOriginValue = () => {
|
|
81
|
+
if (!props.originHref) return props.origin;
|
|
75
82
|
return (
|
|
76
83
|
<a
|
|
77
84
|
class="s-about-modal-row-link"
|
|
78
|
-
href={props.
|
|
85
|
+
href={props.originHref}
|
|
79
86
|
target="_blank"
|
|
80
87
|
rel="noopener noreferrer"
|
|
81
88
|
>
|
|
82
|
-
{props.
|
|
89
|
+
{props.origin}
|
|
83
90
|
</a>
|
|
84
91
|
);
|
|
85
92
|
};
|
|
@@ -113,13 +120,14 @@ export const HkAboutModal = defineComponent({
|
|
|
113
120
|
</div>
|
|
114
121
|
</header>
|
|
115
122
|
|
|
123
|
+
{props.slogan && <p class="s-about-modal-slogan">{props.slogan}</p>}
|
|
116
124
|
{props.description && <p class="s-about-modal-description">{props.description}</p>}
|
|
117
125
|
|
|
118
126
|
<div class="s-about-modal-rows">
|
|
119
|
-
{props.author && (
|
|
127
|
+
{(props.author || props.origin) && (
|
|
120
128
|
<div class="s-about-modal-row">
|
|
121
|
-
<span class="s-about-modal-row-label">{
|
|
122
|
-
<span class="s-about-modal-row-value">{
|
|
129
|
+
<span class="s-about-modal-row-label">{props.author}</span>
|
|
130
|
+
<span class="s-about-modal-row-value">{renderOriginValue()}</span>
|
|
123
131
|
</div>
|
|
124
132
|
)}
|
|
125
133
|
{props.license && (
|
|
@@ -176,6 +184,22 @@ export const HkAboutModal = defineComponent({
|
|
|
176
184
|
</div>
|
|
177
185
|
)}
|
|
178
186
|
|
|
187
|
+
{props.footerLinks.length > 0 && (
|
|
188
|
+
<div class="s-about-modal-footer-links">
|
|
189
|
+
{props.footerLinks.map((link) => (
|
|
190
|
+
<a
|
|
191
|
+
key={link.href}
|
|
192
|
+
href={link.href}
|
|
193
|
+
target="_blank"
|
|
194
|
+
rel="noopener noreferrer"
|
|
195
|
+
class="s-about-modal-footer-link"
|
|
196
|
+
>
|
|
197
|
+
{link.label}
|
|
198
|
+
</a>
|
|
199
|
+
))}
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
|
|
179
203
|
<footer class="s-about-modal-footer">
|
|
180
204
|
<HBadge variant="muted">
|
|
181
205
|
© {new Date().getFullYear()} {props.copyright ?? props.appName}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createApp, defineComponent, h, nextTick, ref } from "vue";
|
|
3
|
+
|
|
4
|
+
import HkModal from "./HkModal";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HkModal footerLead slot contract tests:
|
|
8
|
+
* - the named slot renders inside .hk-modal-footer BEFORE the action buttons,
|
|
9
|
+
* wrapped in .hk-modal-footer-lead, with the band marked .hk-modal-footer--lead
|
|
10
|
+
* - the band still renders with ONLY a footerLead (no footerActions) — a lead
|
|
11
|
+
* alone must not vanish with the actions
|
|
12
|
+
* - absent the slot, no new DOM appears (backward compat)
|
|
13
|
+
* - the explicit `footer` slot keeps precedence over footerLead + footerActions
|
|
14
|
+
* (existing full-custom contract unchanged)
|
|
15
|
+
*/
|
|
16
|
+
const mounts: Array<{ app: ReturnType<typeof createApp>; container: HTMLElement }> = [];
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
for (const { app, container } of mounts.splice(0)) {
|
|
20
|
+
app.unmount();
|
|
21
|
+
container.remove();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
interface Harness {
|
|
26
|
+
container: HTMLElement;
|
|
27
|
+
footer: HTMLElement | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function mountModal(
|
|
31
|
+
props: Record<string, unknown>,
|
|
32
|
+
slots: Record<string, unknown> = {},
|
|
33
|
+
): Promise<Harness> {
|
|
34
|
+
const container = document.createElement("div");
|
|
35
|
+
document.body.appendChild(container);
|
|
36
|
+
const open = ref(true);
|
|
37
|
+
const Wrapper = defineComponent({
|
|
38
|
+
setup() {
|
|
39
|
+
// A FRESH slots object per Wrapper render: updateSlots mutates the
|
|
40
|
+
// slots object it is handed (deleting keys absent from a later
|
|
41
|
+
// sibling mount's shape), and a shared literal would lose footerLead
|
|
42
|
+
// on re-render.
|
|
43
|
+
return () =>
|
|
44
|
+
h(HkModal, {
|
|
45
|
+
...props,
|
|
46
|
+
modelValue: open.value,
|
|
47
|
+
"onUpdate:modelValue": (v: boolean) => { open.value = v; },
|
|
48
|
+
}, { ...slots });
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const app = createApp(Wrapper);
|
|
52
|
+
app.mount(container);
|
|
53
|
+
mounts.push({ app, container });
|
|
54
|
+
await nextTick();
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
56
|
+
await nextTick();
|
|
57
|
+
// HkModal teleports to body and sibling test files in this worker can
|
|
58
|
+
// leave their own modals behind — scope to the NEWEST mounted footer
|
|
59
|
+
// instead of the document-first match.
|
|
60
|
+
const footers = document.querySelectorAll(".hk-modal-footer");
|
|
61
|
+
return {
|
|
62
|
+
container,
|
|
63
|
+
footer: footers.length ? (footers[footers.length - 1] as HTMLElement) : null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe("HkModal footerLead slot", () => {
|
|
68
|
+
it("renders the lead before the action buttons inside the band", async () => {
|
|
69
|
+
const { footer } = await mountModal(
|
|
70
|
+
{
|
|
71
|
+
title: "Config",
|
|
72
|
+
footerActions: [{ label: "Add", variant: "primary" }],
|
|
73
|
+
},
|
|
74
|
+
{ footerLead: () => h("button", { class: "lead-jump", "data-test": "jump" }, "JUMP") },
|
|
75
|
+
);
|
|
76
|
+
expect(footer).not.toBeNull();
|
|
77
|
+
expect(footer?.classList.contains("hk-modal-footer--lead")).toBe(true);
|
|
78
|
+
const lead = footer?.querySelector(".hk-modal-footer-lead");
|
|
79
|
+
expect(lead).not.toBeNull();
|
|
80
|
+
expect(lead?.querySelector(".lead-jump")?.textContent).toBe("JUMP");
|
|
81
|
+
|
|
82
|
+
const children = footer ? Array.from(footer.children) : [];
|
|
83
|
+
expect(children[0]?.classList.contains("hk-modal-footer-lead")).toBe(true);
|
|
84
|
+
expect(children.length).toBe(2);
|
|
85
|
+
expect(children[1]?.textContent).toBe("Add");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("keeps the band alive with ONLY a footerLead and no actions", async () => {
|
|
89
|
+
const { footer } = await mountModal(
|
|
90
|
+
{ title: "Config" },
|
|
91
|
+
{ footerLead: () => h("button", { class: "lead-jump" }, "JUMP") },
|
|
92
|
+
);
|
|
93
|
+
expect(footer).not.toBeNull();
|
|
94
|
+
expect(footer?.classList.contains("hk-modal-footer--lead")).toBe(true);
|
|
95
|
+
expect(footer?.querySelector(".hk-modal-footer-lead")).not.toBeNull();
|
|
96
|
+
// No action buttons — the only children are the lead wrapper.
|
|
97
|
+
const children = footer ? Array.from(footer.children) : [];
|
|
98
|
+
expect(children).toHaveLength(1);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("adds no lead DOM and no band when both lead and actions are absent", async () => {
|
|
102
|
+
const { footer } = await mountModal({ title: "Config" });
|
|
103
|
+
expect(footer).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("the explicit footer slot keeps precedence over footerLead and actions", async () => {
|
|
107
|
+
const { footer } = await mountModal(
|
|
108
|
+
{
|
|
109
|
+
title: "Config",
|
|
110
|
+
footerActions: [{ label: "Add" }],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
footer: () => h("div", { class: "custom-footer" }, "CUSTOM"),
|
|
114
|
+
footerLead: () => h("button", { class: "lead-jump" }, "JUMP"),
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
expect(footer).not.toBeNull();
|
|
118
|
+
expect(footer?.classList.contains("hk-modal-footer--lead")).toBe(false);
|
|
119
|
+
expect(footer?.querySelector(".custom-footer")?.textContent).toBe("CUSTOM");
|
|
120
|
+
expect(footer?.querySelector(".hk-modal-footer-lead")).toBeNull();
|
|
121
|
+
expect(footer?.textContent).not.toContain("Add");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -298,6 +298,21 @@
|
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
// Lead content (e.g. a ghost jump link into the admin console) rendered at
|
|
302
|
+
// the footer's inline-start with the action buttons still pinned at
|
|
303
|
+
// inline-end — the footer counterpart of .hk-modal-header-lead. The lead's
|
|
304
|
+
// auto inline-end margin absorbs the slack (same distribution trick the
|
|
305
|
+
// header uses), so the layout mirrors correctly in RTL without a
|
|
306
|
+
// space-between override.
|
|
307
|
+
.hk-modal-footer--lead {
|
|
308
|
+
.hk-modal-footer-lead {
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
flex-shrink: 0;
|
|
312
|
+
margin-inline-end: auto;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
301
316
|
// ------
|
|
302
317
|
// Responsive — full-screen on narrow viewports
|
|
303
318
|
// ------
|
|
@@ -695,10 +695,13 @@ export default defineComponent({
|
|
|
695
695
|
if (slots.footer) {
|
|
696
696
|
return <div class="hk-modal-footer">{slots.footer()}</div>;
|
|
697
697
|
}
|
|
698
|
-
|
|
698
|
+
const hasLead = Boolean(slots.footerLead);
|
|
699
|
+
const hasActions = Boolean(props.footerActions && props.footerActions.length > 0);
|
|
700
|
+
if (hasLead || hasActions) {
|
|
699
701
|
return (
|
|
700
|
-
<div class="hk-modal-footer">
|
|
701
|
-
{
|
|
702
|
+
<div class={["hk-modal-footer", hasLead ? "hk-modal-footer--lead" : ""]}>
|
|
703
|
+
{hasLead && <div class="hk-modal-footer-lead">{slots.footerLead?.()}</div>}
|
|
704
|
+
{props.footerActions?.map((action, i) => (
|
|
702
705
|
<HButton
|
|
703
706
|
key={i}
|
|
704
707
|
variant={action.variant ?? "secondary"}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "البناء",
|
|
18
18
|
"hikari::about.engineVersion": "إصدار المحرك",
|
|
19
19
|
"hikari::about.engineBuildHash": "بناء المحرك",
|
|
20
|
-
"hikari::about.author": "المؤلف",
|
|
21
20
|
"hikari::about.license": "الترخيص",
|
|
22
21
|
"hikari::theme.mode": "وضع السمة",
|
|
23
22
|
"hikari::theme.modeLight": "فاتح",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "Build",
|
|
18
18
|
"hikari::about.engineVersion": "Engine-Version",
|
|
19
19
|
"hikari::about.engineBuildHash": "Engine-Build",
|
|
20
|
-
"hikari::about.author": "Autor",
|
|
21
20
|
"hikari::about.license": "Lizenz",
|
|
22
21
|
"hikari::theme.mode": "Design-Modus",
|
|
23
22
|
"hikari::theme.modeLight": "Hell",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "Build",
|
|
18
18
|
"hikari::about.engineVersion": "Engine version",
|
|
19
19
|
"hikari::about.engineBuildHash": "Engine build",
|
|
20
|
-
"hikari::about.author": "Author",
|
|
21
20
|
"hikari::about.license": "License",
|
|
22
21
|
"hikari::theme.mode": "Theme mode",
|
|
23
22
|
"hikari::theme.modeLight": "Light",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "Compilación",
|
|
18
18
|
"hikari::about.engineVersion": "Versión del motor",
|
|
19
19
|
"hikari::about.engineBuildHash": "Compilación del motor",
|
|
20
|
-
"hikari::about.author": "Autor",
|
|
21
20
|
"hikari::about.license": "Licencia",
|
|
22
21
|
"hikari::theme.mode": "Modo de tema",
|
|
23
22
|
"hikari::theme.modeLight": "Claro",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "Build",
|
|
18
18
|
"hikari::about.engineVersion": "Version du moteur",
|
|
19
19
|
"hikari::about.engineBuildHash": "Build du moteur",
|
|
20
|
-
"hikari::about.author": "Auteur",
|
|
21
20
|
"hikari::about.license": "Licence",
|
|
22
21
|
"hikari::theme.mode": "Mode du thème",
|
|
23
22
|
"hikari::theme.modeLight": "Clair",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "ビルド",
|
|
18
18
|
"hikari::about.engineVersion": "エンジンバージョン",
|
|
19
19
|
"hikari::about.engineBuildHash": "エンジンビルド",
|
|
20
|
-
"hikari::about.author": "作者",
|
|
21
20
|
"hikari::about.license": "ライセンス",
|
|
22
21
|
"hikari::theme.mode": "テーマモード",
|
|
23
22
|
"hikari::theme.modeLight": "ライト",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "빌드",
|
|
18
18
|
"hikari::about.engineVersion": "엔진 버전",
|
|
19
19
|
"hikari::about.engineBuildHash": "엔진 빌드",
|
|
20
|
-
"hikari::about.author": "제작자",
|
|
21
20
|
"hikari::about.license": "라이선스",
|
|
22
21
|
"hikari::theme.mode": "테마 모드",
|
|
23
22
|
"hikari::theme.modeLight": "라이트",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "Build",
|
|
18
18
|
"hikari::about.engineVersion": "Versão do motor",
|
|
19
19
|
"hikari::about.engineBuildHash": "Build do motor",
|
|
20
|
-
"hikari::about.author": "Autor",
|
|
21
20
|
"hikari::about.license": "Licença",
|
|
22
21
|
"hikari::theme.mode": "Modo do tema",
|
|
23
22
|
"hikari::theme.modeLight": "Claro",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"hikari::about.buildHash": "Сборка",
|
|
18
18
|
"hikari::about.engineVersion": "Версия движка",
|
|
19
19
|
"hikari::about.engineBuildHash": "Сборка движка",
|
|
20
|
-
"hikari::about.author": "Автор",
|
|
21
20
|
"hikari::about.license": "Лицензия",
|
|
22
21
|
"hikari::theme.mode": "Режим темы",
|
|
23
22
|
"hikari::theme.modeLight": "Светлая",
|