@mangtre/ui 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/LICENSE +26 -0
- package/dist/MangThemeProvider-BqdGKBP2.d.ts +34 -0
- package/dist/Money-Dw3GnUvX.d.ts +27 -0
- package/dist/actions.d.ts +31 -0
- package/dist/actions.js +13 -0
- package/dist/ai.d.ts +58 -0
- package/dist/ai.js +13 -0
- package/dist/analytics.d.ts +40 -0
- package/dist/analytics.js +15 -0
- package/dist/app.d.ts +24 -0
- package/dist/app.js +9 -0
- package/dist/catalog.d.ts +76 -0
- package/dist/catalog.js +1210 -0
- package/dist/charts.d.ts +89 -0
- package/dist/charts.js +34 -0
- package/dist/chunk-3AL4SUFD.js +301 -0
- package/dist/chunk-4XNSYKQE.js +142 -0
- package/dist/chunk-5Z4VLQKH.js +43 -0
- package/dist/chunk-7P2EQZYD.js +59 -0
- package/dist/chunk-7WHNIEDV.js +120 -0
- package/dist/chunk-ASZKHSMG.js +82 -0
- package/dist/chunk-BCBN2EGH.js +216 -0
- package/dist/chunk-BLYAFV45.js +320 -0
- package/dist/chunk-DLKEXWPA.js +90 -0
- package/dist/chunk-DTASXPTB.js +70 -0
- package/dist/chunk-FZRXVRC7.js +63 -0
- package/dist/chunk-ID233AGM.js +108 -0
- package/dist/chunk-IVYXOKMO.js +74 -0
- package/dist/chunk-IX3DYETF.js +61 -0
- package/dist/chunk-JJB4PJC3.js +166 -0
- package/dist/chunk-K5Q3RCV6.js +119 -0
- package/dist/chunk-LNRUPJDF.js +161 -0
- package/dist/chunk-LZORNMBL.js +0 -0
- package/dist/chunk-OBPXCUVF.js +282 -0
- package/dist/chunk-OJX2EIMB.js +145 -0
- package/dist/chunk-PPOYMKV3.js +170 -0
- package/dist/chunk-PQGUWJG4.js +47 -0
- package/dist/chunk-RE7OWRA4.js +187 -0
- package/dist/chunk-SJF3CHAW.js +108 -0
- package/dist/chunk-UF6ANDJZ.js +112 -0
- package/dist/chunk-VGC5DMOM.js +107 -0
- package/dist/chunk-VP56Z4BS.js +0 -0
- package/dist/chunk-VRD66FIA.js +77 -0
- package/dist/chunk-X7T2DJLU.js +113 -0
- package/dist/chunk-XPV3OOLU.js +147 -0
- package/dist/chunk-YN5O6YL6.js +69 -0
- package/dist/chunk-Z4ANGBPC.js +94 -0
- package/dist/creator.d.ts +55 -0
- package/dist/creator.js +20 -0
- package/dist/data-room.d.ts +50 -0
- package/dist/data-room.js +17 -0
- package/dist/editor.d.ts +32 -0
- package/dist/editor.js +14 -0
- package/dist/feedback.d.ts +48 -0
- package/dist/feedback.js +16 -0
- package/dist/forms.d.ts +91 -0
- package/dist/forms.js +26 -0
- package/dist/handoff.d.ts +37 -0
- package/dist/handoff.js +13 -0
- package/dist/index.css +2 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +338 -0
- package/dist/layout.d.ts +57 -0
- package/dist/layout.js +22 -0
- package/dist/learning.d.ts +46 -0
- package/dist/learning.js +15 -0
- package/dist/media.d.ts +48 -0
- package/dist/media.js +16 -0
- package/dist/monetization.d.ts +30 -0
- package/dist/monetization.js +14 -0
- package/dist/money.d.ts +45 -0
- package/dist/money.js +28 -0
- package/dist/navigation.d.ts +36 -0
- package/dist/navigation.js +14 -0
- package/dist/overlay.d.ts +72 -0
- package/dist/overlay.js +20 -0
- package/dist/platform.d.ts +94 -0
- package/dist/platform.js +42 -0
- package/dist/primitives.d.ts +83 -0
- package/dist/primitives.js +22 -0
- package/dist/privacy.d.ts +28 -0
- package/dist/privacy.js +15 -0
- package/dist/sandbox.d.ts +40 -0
- package/dist/sandbox.js +15 -0
- package/dist/settings.d.ts +29 -0
- package/dist/settings.js +13 -0
- package/dist/surface.d.ts +33 -0
- package/dist/surface.js +16 -0
- package/dist/theme.css +63 -0
- package/dist/theme.d.ts +64 -0
- package/dist/theme.js +27 -0
- package/dist/tokens.css +119 -0
- package/dist/tokens.d.ts +128 -0
- package/dist/tokens.js +8 -0
- package/package.json +151 -0
package/dist/charts.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { T as ThemeMode } from './MangThemeProvider-BqdGKBP2.js';
|
|
4
|
+
import '@mangtre/core';
|
|
5
|
+
|
|
6
|
+
/** Brand-palette series cycle (no dedicated chart tokens needed). */
|
|
7
|
+
declare const SERIES: readonly ["#37B693", "#E8C547", "#B5793A", "#2E7CB8", "#8FE3B8", "#1F7A5C"];
|
|
8
|
+
interface ThemedProps {
|
|
9
|
+
theme?: ThemeMode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
interface StatProps extends ThemedProps {
|
|
13
|
+
label: ReactNode;
|
|
14
|
+
value: ReactNode;
|
|
15
|
+
/** Optional delta (e.g. "+12%"); `direction` colors it. */
|
|
16
|
+
delta?: ReactNode;
|
|
17
|
+
direction?: "up" | "down";
|
|
18
|
+
}
|
|
19
|
+
declare function Stat({ label, value, delta, direction, theme, className }: StatProps): react.JSX.Element;
|
|
20
|
+
declare function StatGroup({ children, theme, className }: ThemedProps & {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}): react.JSX.Element;
|
|
23
|
+
interface SparklineProps extends ThemedProps {
|
|
24
|
+
values: number[];
|
|
25
|
+
width?: number;
|
|
26
|
+
height?: number;
|
|
27
|
+
/** Series color index (cycles the brand palette). */
|
|
28
|
+
colorIndex?: number;
|
|
29
|
+
}
|
|
30
|
+
declare function Sparkline({ values, width, height, colorIndex, theme, className, }: SparklineProps): react.JSX.Element;
|
|
31
|
+
interface BarChartProps extends ThemedProps {
|
|
32
|
+
values: number[];
|
|
33
|
+
labels?: string[];
|
|
34
|
+
}
|
|
35
|
+
declare function BarChart({ values, labels, theme, className }: BarChartProps): react.JSX.Element;
|
|
36
|
+
interface LineChartProps extends ThemedProps {
|
|
37
|
+
values: number[];
|
|
38
|
+
width?: number;
|
|
39
|
+
height?: number;
|
|
40
|
+
colorIndex?: number;
|
|
41
|
+
/** Fill the area under the line. */
|
|
42
|
+
area?: boolean;
|
|
43
|
+
}
|
|
44
|
+
declare function LineChart({ values, width, height, colorIndex, area, theme, className, }: LineChartProps): react.JSX.Element;
|
|
45
|
+
/** Area chart = line chart with the under-fill on. */
|
|
46
|
+
declare function AreaChart(props: Omit<LineChartProps, "area">): react.JSX.Element;
|
|
47
|
+
interface DonutChartProps extends ThemedProps {
|
|
48
|
+
values: number[];
|
|
49
|
+
size?: number;
|
|
50
|
+
/** Stroke thickness (donut hole size). */
|
|
51
|
+
thickness?: number;
|
|
52
|
+
center?: ReactNode;
|
|
53
|
+
}
|
|
54
|
+
declare function DonutChart({ values, size, thickness, center, theme, className, }: DonutChartProps): react.JSX.Element;
|
|
55
|
+
interface HeatmapProps extends ThemedProps {
|
|
56
|
+
/** Intensity 0–1 per cell, laid out in columns of 7 (GitHub-style weeks). */
|
|
57
|
+
values: number[];
|
|
58
|
+
}
|
|
59
|
+
declare function Heatmap({ values, theme, className }: HeatmapProps): react.JSX.Element;
|
|
60
|
+
interface LegendItem {
|
|
61
|
+
label: ReactNode;
|
|
62
|
+
colorIndex?: number;
|
|
63
|
+
}
|
|
64
|
+
declare function Legend({ items, theme, className }: ThemedProps & {
|
|
65
|
+
items: LegendItem[];
|
|
66
|
+
}): react.JSX.Element;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Pure geometry helpers for the lightweight charts (no charting dependency). Unit-tested.
|
|
70
|
+
* All charts render inline SVG sized to a viewBox, so callers stay resolution-independent.
|
|
71
|
+
*/
|
|
72
|
+
/** Largest value (≥ 1) used as the axis max so a flat all-zero series still renders. */
|
|
73
|
+
declare function axisMax(values: readonly number[]): number;
|
|
74
|
+
/** Map values to SVG polyline points within a `w×h` box (y inverted). */
|
|
75
|
+
declare function linePoints(values: readonly number[], w: number, h: number, pad?: number): string;
|
|
76
|
+
/** Bar heights as a fraction (0–1) of the box, one per value. */
|
|
77
|
+
declare function barFractions(values: readonly number[]): number[];
|
|
78
|
+
interface Segment {
|
|
79
|
+
/** Start fraction of the circle (0–1). */
|
|
80
|
+
start: number;
|
|
81
|
+
/** End fraction of the circle (0–1). */
|
|
82
|
+
end: number;
|
|
83
|
+
/** This slice's share of the total (0–1). */
|
|
84
|
+
frac: number;
|
|
85
|
+
}
|
|
86
|
+
/** Cumulative segments for a donut/pie. Empty/zero total → no segments. */
|
|
87
|
+
declare function pieSegments(values: readonly number[]): Segment[];
|
|
88
|
+
|
|
89
|
+
export { AreaChart, BarChart, type BarChartProps, DonutChart, type DonutChartProps, Heatmap, type HeatmapProps, Legend, type LegendItem, LineChart, type LineChartProps, SERIES, type Segment, Sparkline, type SparklineProps, Stat, StatGroup, type StatProps, axisMax, barFractions, linePoints, pieSegments };
|
package/dist/charts.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AreaChart,
|
|
3
|
+
BarChart,
|
|
4
|
+
DonutChart,
|
|
5
|
+
Heatmap,
|
|
6
|
+
Legend,
|
|
7
|
+
LineChart,
|
|
8
|
+
SERIES,
|
|
9
|
+
Sparkline,
|
|
10
|
+
Stat,
|
|
11
|
+
StatGroup,
|
|
12
|
+
axisMax,
|
|
13
|
+
barFractions,
|
|
14
|
+
linePoints,
|
|
15
|
+
pieSegments
|
|
16
|
+
} from "./chunk-BLYAFV45.js";
|
|
17
|
+
import "./chunk-3AL4SUFD.js";
|
|
18
|
+
import "./chunk-PPOYMKV3.js";
|
|
19
|
+
export {
|
|
20
|
+
AreaChart,
|
|
21
|
+
BarChart,
|
|
22
|
+
DonutChart,
|
|
23
|
+
Heatmap,
|
|
24
|
+
Legend,
|
|
25
|
+
LineChart,
|
|
26
|
+
SERIES,
|
|
27
|
+
Sparkline,
|
|
28
|
+
Stat,
|
|
29
|
+
StatGroup,
|
|
30
|
+
axisMax,
|
|
31
|
+
barFractions,
|
|
32
|
+
linePoints,
|
|
33
|
+
pieSegments
|
|
34
|
+
};
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tokens
|
|
3
|
+
} from "./chunk-PPOYMKV3.js";
|
|
4
|
+
|
|
5
|
+
// src/scopedStyle.ts
|
|
6
|
+
var scopedStyleRefs = /* @__PURE__ */ new Map();
|
|
7
|
+
function mountScopedStyle(id, css) {
|
|
8
|
+
let styleEl = document.getElementById(id);
|
|
9
|
+
if (!styleEl) {
|
|
10
|
+
styleEl = document.createElement("style");
|
|
11
|
+
styleEl.id = id;
|
|
12
|
+
document.head.appendChild(styleEl);
|
|
13
|
+
}
|
|
14
|
+
styleEl.textContent = css;
|
|
15
|
+
scopedStyleRefs.set(id, (scopedStyleRefs.get(id) ?? 0) + 1);
|
|
16
|
+
let disposed = false;
|
|
17
|
+
return () => {
|
|
18
|
+
if (disposed) return;
|
|
19
|
+
disposed = true;
|
|
20
|
+
const next = (scopedStyleRefs.get(id) ?? 1) - 1;
|
|
21
|
+
if (next > 0) {
|
|
22
|
+
scopedStyleRefs.set(id, next);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
scopedStyleRefs.delete(id);
|
|
26
|
+
document.getElementById(id)?.remove();
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/theme/MangThemeProvider.tsx
|
|
31
|
+
import { createContext, createElement, useContext, useMemo } from "react";
|
|
32
|
+
var DEFAULTS = { theme: "light", locale: "vi", density: "comfortable" };
|
|
33
|
+
var MangThemeContext = createContext(DEFAULTS);
|
|
34
|
+
function MangThemeProvider({
|
|
35
|
+
theme = "light",
|
|
36
|
+
locale = "vi",
|
|
37
|
+
density = "comfortable",
|
|
38
|
+
children
|
|
39
|
+
}) {
|
|
40
|
+
const value = useMemo(
|
|
41
|
+
() => ({ theme, locale, density }),
|
|
42
|
+
[theme, locale, density]
|
|
43
|
+
);
|
|
44
|
+
return createElement(MangThemeContext.Provider, { value }, children);
|
|
45
|
+
}
|
|
46
|
+
function useMangTheme() {
|
|
47
|
+
return useContext(MangThemeContext);
|
|
48
|
+
}
|
|
49
|
+
function useResolvedTheme(override) {
|
|
50
|
+
return override ?? useContext(MangThemeContext).theme;
|
|
51
|
+
}
|
|
52
|
+
function useLocale() {
|
|
53
|
+
return useContext(MangThemeContext).locale;
|
|
54
|
+
}
|
|
55
|
+
function useDensity() {
|
|
56
|
+
return useContext(MangThemeContext).density;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/theme/useScopedStyle.ts
|
|
60
|
+
import { useEffect } from "react";
|
|
61
|
+
function useScopedStyle(id, css) {
|
|
62
|
+
useEffect(() => mountScopedStyle(id, css), [id, css]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/theme/kit.ts
|
|
66
|
+
var KIT_STYLE_ID = "mang-ui-kit";
|
|
67
|
+
var { color, semantic, radius, shadow, motion, fontSize, fontWeight, lineHeight, font, z } = tokens;
|
|
68
|
+
var head = `"${font.heading}", system-ui, sans-serif`;
|
|
69
|
+
var body = `"${font.body}", system-ui, sans-serif`;
|
|
70
|
+
var ease = motion.ease.standard;
|
|
71
|
+
var mix = (a, pct, b) => `color-mix(in oklab, ${a} ${pct}%, ${b})`;
|
|
72
|
+
var DARK_VARS = (
|
|
73
|
+
/* css */
|
|
74
|
+
`
|
|
75
|
+
--m-surface: ${mix(color.mangGreen, 8, color.dark)};
|
|
76
|
+
--m-sunken: ${color.dark};
|
|
77
|
+
--m-raised: ${mix(color.mangGreen, 13, color.dark)};
|
|
78
|
+
--m-text: ${color.paper};
|
|
79
|
+
--m-text-strong: #ffffff;
|
|
80
|
+
--m-text-muted: ${mix(color.paper, 60, color.dark)};
|
|
81
|
+
--m-line: ${mix(color.shoot, 22, color.dark)};
|
|
82
|
+
--m-line-strong: ${mix(color.shoot, 40, color.dark)};
|
|
83
|
+
--m-accent-hover: ${color.shoot};
|
|
84
|
+
--m-accent-soft: ${mix(color.mangGreen, 24, color.dark)};
|
|
85
|
+
--m-success-soft: ${mix(semantic.state.success, 26, color.dark)};
|
|
86
|
+
--m-success-on: ${mix(semantic.state.success, 52, "#ffffff")};
|
|
87
|
+
--m-danger-soft: ${mix(semantic.state.danger, 26, color.dark)};
|
|
88
|
+
--m-danger-on: ${mix(semantic.state.danger, 55, "#ffffff")};
|
|
89
|
+
--m-warning-soft: ${mix(semantic.state.warning, 26, color.dark)};
|
|
90
|
+
--m-warning-on: ${mix(semantic.state.warning, 58, "#ffffff")};
|
|
91
|
+
--m-info-soft: ${mix(semantic.state.info, 26, color.dark)};
|
|
92
|
+
--m-info-on: ${mix(semantic.state.info, 58, "#ffffff")};
|
|
93
|
+
--m-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.55);
|
|
94
|
+
`
|
|
95
|
+
);
|
|
96
|
+
var KIT_CSS = (
|
|
97
|
+
/* css */
|
|
98
|
+
`
|
|
99
|
+
.mang-ui-scope {
|
|
100
|
+
--m-surface: ${semantic.surface.base};
|
|
101
|
+
--m-sunken: ${semantic.surface.sunken};
|
|
102
|
+
--m-raised: ${semantic.surface.base};
|
|
103
|
+
--m-text: ${semantic.content.default};
|
|
104
|
+
--m-text-strong: ${semantic.content.strong};
|
|
105
|
+
--m-text-muted: ${semantic.content.muted};
|
|
106
|
+
--m-line: ${semantic.line.default};
|
|
107
|
+
--m-line-strong: ${semantic.line.strong};
|
|
108
|
+
--m-accent: ${color.mangGreen};
|
|
109
|
+
--m-accent-hover: ${semantic.accent.hover};
|
|
110
|
+
--m-accent-soft: ${semantic.accent.soft};
|
|
111
|
+
--m-on-accent: ${semantic.content.onAccent};
|
|
112
|
+
--m-success: ${semantic.state.success}; --m-success-soft: ${semantic.state.successSoft}; --m-success-on: ${semantic.state.success};
|
|
113
|
+
--m-danger: ${semantic.state.danger}; --m-danger-soft: ${semantic.state.dangerSoft}; --m-danger-on: ${semantic.state.danger};
|
|
114
|
+
--m-warning: ${semantic.state.warning}; --m-warning-soft: ${semantic.state.warningSoft}; --m-warning-on: ${semantic.state.warning};
|
|
115
|
+
--m-info: ${semantic.state.info}; --m-info-soft: ${semantic.state.infoSoft}; --m-info-on: ${semantic.state.info};
|
|
116
|
+
--m-radius: ${radius.lg};
|
|
117
|
+
--m-radius-sm: ${radius.md};
|
|
118
|
+
--m-shadow: ${shadow.md};
|
|
119
|
+
--m-pad: 0.62rem 0.85rem;
|
|
120
|
+
--m-h: 2.5rem;
|
|
121
|
+
font-family: ${body};
|
|
122
|
+
color: var(--m-text);
|
|
123
|
+
}
|
|
124
|
+
.mang-ui-scope[data-density="compact"] { --m-pad: 0.45rem 0.7rem; --m-h: 2.15rem; }
|
|
125
|
+
.mang-ui-scope[data-density="touch"] { --m-pad: 0.8rem 1rem; --m-h: 3rem; }
|
|
126
|
+
.mang-ui-scope[data-theme="dark"] {${DARK_VARS}}
|
|
127
|
+
@media (prefers-color-scheme: dark) { .mang-ui-scope[data-theme="auto"] {${DARK_VARS}} }
|
|
128
|
+
|
|
129
|
+
/* ---- text ---- */
|
|
130
|
+
.mang-text { margin: 0; font-family: ${body}; color: var(--m-text); line-height: ${lineHeight.normal}; }
|
|
131
|
+
.mang-text[data-tone="muted"] { color: var(--m-text-muted); }
|
|
132
|
+
.mang-text[data-tone="strong"] { color: var(--m-text-strong); }
|
|
133
|
+
.mang-text[data-tone="accent"] { color: var(--m-accent); }
|
|
134
|
+
.mang-heading { margin: 0; font-family: ${head}; font-weight: ${fontWeight.bold}; color: var(--m-text-strong); line-height: ${lineHeight.tight}; letter-spacing: 0.005em; }
|
|
135
|
+
.mang-divider { border: none; border-top: 1px solid var(--m-line); margin: 0; }
|
|
136
|
+
|
|
137
|
+
/* ---- button ---- */
|
|
138
|
+
.mang-btn {
|
|
139
|
+
appearance: none; box-sizing: border-box; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
|
|
140
|
+
min-height: var(--m-h); padding: 0 1.05rem; border-radius: ${radius.md}; border: 1.5px solid transparent;
|
|
141
|
+
font-family: ${head}; font-weight: ${fontWeight.bold}; font-size: ${fontSize.sm}; line-height: 1; text-decoration: none; white-space: nowrap;
|
|
142
|
+
transition: transform ${motion.duration.fast} ${ease}, background ${motion.duration.fast} ${ease}, border-color ${motion.duration.fast} ${ease}, box-shadow ${motion.duration.fast} ${ease}, color ${motion.duration.fast} ${ease};
|
|
143
|
+
}
|
|
144
|
+
.mang-btn[data-size="sm"] { min-height: 2.05rem; padding: 0 0.8rem; font-size: ${fontSize.xs}; }
|
|
145
|
+
.mang-btn[data-size="lg"] { min-height: 2.95rem; padding: 0 1.4rem; font-size: ${fontSize.base}; }
|
|
146
|
+
.mang-btn[data-block="true"] { display: flex; width: 100%; }
|
|
147
|
+
.mang-btn:focus-visible { outline: 3px solid ${mix(color.mangGreen, 45, "transparent")}; outline-offset: 2px; }
|
|
148
|
+
.mang-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
149
|
+
.mang-btn:not(:disabled):active { transform: translateY(0) scale(0.98); }
|
|
150
|
+
.mang-btn[data-variant="primary"] { background: var(--m-accent); color: var(--m-on-accent); box-shadow: 0 10px 22px -12px var(--m-accent); }
|
|
151
|
+
.mang-btn[data-variant="primary"]:not(:disabled):hover { background: var(--m-accent-hover); transform: translateY(-1px); }
|
|
152
|
+
.mang-btn[data-variant="secondary"] { background: var(--m-surface); color: var(--m-text-strong); border-color: var(--m-line-strong); }
|
|
153
|
+
.mang-btn[data-variant="secondary"]:not(:disabled):hover { border-color: var(--m-accent); transform: translateY(-1px); }
|
|
154
|
+
.mang-btn[data-variant="ghost"] { background: transparent; color: var(--m-text-strong); }
|
|
155
|
+
.mang-btn[data-variant="ghost"]:not(:disabled):hover { background: var(--m-accent-soft); }
|
|
156
|
+
.mang-btn[data-variant="danger"] { background: var(--m-danger); color: #fff; }
|
|
157
|
+
.mang-btn[data-variant="danger"]:not(:disabled):hover { filter: brightness(1.06); transform: translateY(-1px); }
|
|
158
|
+
.mang-iconbtn { width: var(--m-h); min-height: var(--m-h); padding: 0; border-radius: ${radius.md}; }
|
|
159
|
+
.mang-iconbtn[data-size="sm"] { width: 2.05rem; min-height: 2.05rem; }
|
|
160
|
+
.mang-btn-group { display: inline-flex; gap: 0.4rem; flex-wrap: wrap; }
|
|
161
|
+
|
|
162
|
+
/* ---- field + controls ---- */
|
|
163
|
+
.mang-field { display: grid; gap: 0.3rem; }
|
|
164
|
+
.mang-field-label { font-family: ${head}; font-weight: ${fontWeight.semibold}; font-size: ${fontSize.sm}; color: var(--m-text-strong); }
|
|
165
|
+
.mang-field-req { color: var(--m-danger); margin-left: 0.15rem; }
|
|
166
|
+
.mang-field-desc { font-size: ${fontSize.xs}; color: var(--m-text-muted); }
|
|
167
|
+
.mang-field-error { font-size: ${fontSize.xs}; color: var(--m-danger-on); font-weight: ${fontWeight.semibold}; }
|
|
168
|
+
.mang-control {
|
|
169
|
+
box-sizing: border-box; width: 100%; min-height: var(--m-h); padding: var(--m-pad); border-radius: ${radius.md};
|
|
170
|
+
border: 1.5px solid var(--m-line-strong); background: var(--m-surface); color: var(--m-text);
|
|
171
|
+
font-family: ${body}; font-size: ${fontSize.base}; line-height: ${lineHeight.normal};
|
|
172
|
+
transition: border-color ${motion.duration.fast} ${ease}, box-shadow ${motion.duration.fast} ${ease};
|
|
173
|
+
}
|
|
174
|
+
.mang-control::placeholder { color: var(--m-text-muted); }
|
|
175
|
+
.mang-control:focus-visible { outline: none; border-color: var(--m-accent); box-shadow: 0 0 0 3px ${mix(color.mangGreen, 35, "transparent")}; }
|
|
176
|
+
.mang-control:disabled { opacity: 0.55; cursor: not-allowed; }
|
|
177
|
+
.mang-control[aria-invalid="true"] { border-color: var(--m-danger); }
|
|
178
|
+
textarea.mang-control { min-height: 5rem; resize: vertical; }
|
|
179
|
+
select.mang-control { cursor: pointer; appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--m-text-muted) 50%), linear-gradient(135deg, var(--m-text-muted) 50%, transparent 50%); background-position: calc(100% - 18px) center, calc(100% - 13px) center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 2rem; }
|
|
180
|
+
.mang-checkbox { display: inline-flex; align-items: center; gap: 0.5rem; cursor: pointer; font-size: ${fontSize.sm}; color: var(--m-text); }
|
|
181
|
+
.mang-checkbox input { width: 1.15rem; height: 1.15rem; accent-color: var(--m-accent); cursor: pointer; }
|
|
182
|
+
.mang-checkbox[data-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
|
|
183
|
+
.mang-switch { position: relative; display: inline-flex; align-items: center; gap: 0.55rem; cursor: pointer; font-size: ${fontSize.sm}; color: var(--m-text); }
|
|
184
|
+
.mang-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
|
|
185
|
+
.mang-switch-track { width: 2.5rem; height: 1.45rem; border-radius: ${radius.full}; background: var(--m-line-strong); transition: background ${motion.duration.base} ${ease}; flex: none; }
|
|
186
|
+
.mang-switch-thumb { position: absolute; left: 3px; top: 50%; width: 1.05rem; height: 1.05rem; border-radius: 50%; background: #fff; transform: translateY(-50%); transition: transform ${motion.duration.base} ${ease}; box-shadow: 0 1px 3px rgba(0,0,0,0.3); }
|
|
187
|
+
.mang-switch input:checked ~ .mang-switch-track { background: var(--m-accent); }
|
|
188
|
+
.mang-switch input:checked ~ .mang-switch-thumb { transform: translate(1.05rem, -50%); }
|
|
189
|
+
.mang-switch input:focus-visible ~ .mang-switch-track { outline: 3px solid ${mix(color.mangGreen, 45, "transparent")}; outline-offset: 2px; }
|
|
190
|
+
.mang-switch[data-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
|
|
191
|
+
|
|
192
|
+
/* ---- surfaces ---- */
|
|
193
|
+
.mang-card { box-sizing: border-box; background: var(--m-surface); border: 1px solid var(--m-line); border-radius: var(--m-radius); box-shadow: var(--m-shadow); color: var(--m-text); overflow: clip; }
|
|
194
|
+
.mang-card[data-interactive="true"] { cursor: pointer; transition: transform ${motion.duration.base} ${ease}, border-color ${motion.duration.base} ${ease}; }
|
|
195
|
+
.mang-card[data-interactive="true"]:hover { transform: translateY(-2px); border-color: ${mix(color.mangGreen, 50, "transparent")}; }
|
|
196
|
+
.mang-card-head { padding: 1rem 1.15rem 0.6rem; }
|
|
197
|
+
.mang-card-body { padding: 0.6rem 1.15rem 1rem; }
|
|
198
|
+
.mang-card-foot { padding: 0.7rem 1.15rem; border-top: 1px solid var(--m-line); display: flex; gap: 0.5rem; align-items: center; }
|
|
199
|
+
.mang-panel { box-sizing: border-box; background: var(--m-sunken); border: 1px solid var(--m-line); border-radius: var(--m-radius); padding: 1rem 1.1rem; color: var(--m-text); }
|
|
200
|
+
|
|
201
|
+
/* ---- tabs ---- */
|
|
202
|
+
.mang-tablist { display: inline-flex; gap: 0.2rem; padding: 0.25rem; background: var(--m-sunken); border-radius: ${radius.md}; border: 1px solid var(--m-line); }
|
|
203
|
+
.mang-tab { appearance: none; cursor: pointer; border: none; background: transparent; padding: 0.45rem 0.85rem; border-radius: ${radius.sm}; font-family: ${head}; font-weight: ${fontWeight.semibold}; font-size: ${fontSize.sm}; color: var(--m-text-muted); transition: background ${motion.duration.fast} ${ease}, color ${motion.duration.fast} ${ease}; }
|
|
204
|
+
.mang-tab:hover { color: var(--m-text-strong); }
|
|
205
|
+
.mang-tab[aria-selected="true"] { background: var(--m-surface); color: var(--m-accent); box-shadow: var(--m-shadow); }
|
|
206
|
+
.mang-tab:focus-visible { outline: 3px solid ${mix(color.mangGreen, 45, "transparent")}; outline-offset: 2px; }
|
|
207
|
+
.mang-tabpanel { padding-top: 0.85rem; }
|
|
208
|
+
|
|
209
|
+
/* ---- overlay: dialog + sheet ---- */
|
|
210
|
+
.mang-overlay-backdrop { position: fixed; inset: 0; z-index: ${z.modal}; display: flex; background: rgba(11,20,18,0.5); backdrop-filter: blur(2px); animation: mang-fade ${motion.duration.base} ${ease}; }
|
|
211
|
+
.mang-dialog { margin: auto; width: min(34rem, calc(100vw - 2rem)); max-height: calc(100dvh - 2rem); overflow: auto; background: var(--m-surface); color: var(--m-text); border: 1px solid var(--m-line); border-radius: ${radius.xl}; box-shadow: var(--m-shadow); animation: mang-pop ${motion.duration.base} ${ease}; }
|
|
212
|
+
.mang-dialog-head, .mang-sheet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; padding: 1.1rem 1.2rem 0.5rem; }
|
|
213
|
+
.mang-dialog-title, .mang-sheet-title { margin: 0; font-family: ${head}; font-weight: ${fontWeight.bold}; font-size: ${fontSize.lg}; color: var(--m-text-strong); }
|
|
214
|
+
.mang-dialog-body, .mang-sheet-body { padding: 0.5rem 1.2rem 1rem; }
|
|
215
|
+
.mang-dialog-foot, .mang-sheet-foot { display: flex; gap: 0.5rem; justify-content: flex-end; padding: 0.5rem 1.2rem 1.1rem; }
|
|
216
|
+
.mang-overlay-backdrop[data-variant="sheet"] { padding: 0; }
|
|
217
|
+
.mang-sheet { background: var(--m-surface); color: var(--m-text); box-shadow: var(--m-shadow); display: flex; flex-direction: column; max-height: 100dvh; }
|
|
218
|
+
.mang-sheet[data-side="right"] { margin-left: auto; height: 100dvh; width: min(28rem, 100vw); border-left: 1px solid var(--m-line); animation: mang-slide-r ${motion.duration.base} ${ease}; }
|
|
219
|
+
.mang-sheet[data-side="left"] { margin-right: auto; height: 100dvh; width: min(28rem, 100vw); border-right: 1px solid var(--m-line); animation: mang-slide-l ${motion.duration.base} ${ease}; }
|
|
220
|
+
.mang-sheet[data-side="bottom"] { margin-top: auto; width: 100%; max-height: 85dvh; border-top: 1px solid var(--m-line); border-radius: ${radius.xl} ${radius.xl} 0 0; animation: mang-slide-b ${motion.duration.base} ${ease}; }
|
|
221
|
+
@keyframes mang-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
222
|
+
@keyframes mang-pop { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
|
|
223
|
+
@keyframes mang-slide-r { from { transform: translateX(100%); } to { transform: none; } }
|
|
224
|
+
@keyframes mang-slide-l { from { transform: translateX(-100%); } to { transform: none; } }
|
|
225
|
+
@keyframes mang-slide-b { from { transform: translateY(100%); } to { transform: none; } }
|
|
226
|
+
|
|
227
|
+
/* ---- toast ---- */
|
|
228
|
+
.mang-toast-viewport { position: fixed; z-index: ${z.toast}; right: 1rem; bottom: 1rem; display: flex; flex-direction: column; gap: 0.5rem; max-width: min(24rem, calc(100vw - 2rem)); }
|
|
229
|
+
.mang-toast { display: flex; align-items: flex-start; gap: 0.6rem; padding: 0.75rem 0.9rem; border-radius: ${radius.md}; background: var(--m-surface); color: var(--m-text); border: 1px solid var(--m-line); border-left: 4px solid var(--m-accent); box-shadow: var(--m-shadow); animation: mang-pop ${motion.duration.base} ${ease}; }
|
|
230
|
+
.mang-toast[data-tone="success"] { border-left-color: var(--m-success); }
|
|
231
|
+
.mang-toast[data-tone="danger"] { border-left-color: var(--m-danger); }
|
|
232
|
+
.mang-toast[data-tone="warning"] { border-left-color: var(--m-warning); }
|
|
233
|
+
.mang-toast-msg { font-size: ${fontSize.sm}; line-height: ${lineHeight.snug}; }
|
|
234
|
+
|
|
235
|
+
/* ---- feedback ---- */
|
|
236
|
+
.mang-empty, .mang-error-state { display: grid; gap: 0.5rem; justify-items: center; text-align: center; padding: 2rem 1.25rem; color: var(--m-text-muted); }
|
|
237
|
+
.mang-empty-icon { font-size: 2rem; line-height: 1; }
|
|
238
|
+
.mang-empty-title { font-family: ${head}; font-weight: ${fontWeight.bold}; font-size: ${fontSize.lg}; color: var(--m-text-strong); }
|
|
239
|
+
.mang-error-state { color: var(--m-danger-on); }
|
|
240
|
+
.mang-error-state .mang-empty-title { color: var(--m-danger-on); }
|
|
241
|
+
.mang-skeleton { display: block; border-radius: ${radius.sm}; background: linear-gradient(90deg, var(--m-sunken) 25%, ${mix(color.mangGreen, 8, "transparent")} 50%, var(--m-sunken) 75%); background-size: 200% 100%; animation: mang-shimmer 1.3s linear infinite; }
|
|
242
|
+
@keyframes mang-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
|
|
243
|
+
.mang-spinner { display: inline-block; width: 1.25rem; height: 1.25rem; border-radius: 50%; border: 2.5px solid ${mix(color.mangGreen, 25, "transparent")}; border-top-color: var(--m-accent); animation: mang-spin 0.7s linear infinite; }
|
|
244
|
+
@keyframes mang-spin { to { transform: rotate(360deg); } }
|
|
245
|
+
.mang-progress { width: 100%; height: 0.55rem; border-radius: ${radius.full}; background: var(--m-sunken); overflow: hidden; }
|
|
246
|
+
.mang-progress-bar { height: 100%; border-radius: ${radius.full}; background: var(--m-accent); transition: width ${motion.duration.slow} ${ease}; }
|
|
247
|
+
.mang-progress[data-indeterminate="true"] .mang-progress-bar { width: 40% !important; animation: mang-indet 1.1s ${ease} infinite; }
|
|
248
|
+
@keyframes mang-indet { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }
|
|
249
|
+
|
|
250
|
+
/* ---- badges ---- */
|
|
251
|
+
.mang-badge { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.18rem 0.55rem; border-radius: ${radius.full}; font-family: ${head}; font-weight: ${fontWeight.semibold}; font-size: ${fontSize.xs}; line-height: 1.4; background: var(--m-accent-soft); color: var(--m-accent); }
|
|
252
|
+
.mang-badge[data-tone="neutral"] { background: var(--m-sunken); color: var(--m-text-muted); }
|
|
253
|
+
.mang-badge[data-tone="success"] { background: var(--m-success-soft); color: var(--m-success-on); }
|
|
254
|
+
.mang-badge[data-tone="danger"] { background: var(--m-danger-soft); color: var(--m-danger-on); }
|
|
255
|
+
.mang-badge[data-tone="warning"] { background: var(--m-warning-soft); color: var(--m-warning-on); }
|
|
256
|
+
.mang-badge[data-tone="info"] { background: var(--m-info-soft); color: var(--m-info-on); }
|
|
257
|
+
.mang-badge svg { flex: none; }
|
|
258
|
+
.mang-verified { background: ${mix(color.mangGreen, 15, "transparent")}; color: var(--m-accent); box-shadow: inset 0 0 0 1px ${mix(color.mangGreen, 25, "transparent")}; }
|
|
259
|
+
.mang-money { font-variant-numeric: tabular-nums; font-weight: ${fontWeight.semibold}; color: var(--m-text-strong); }
|
|
260
|
+
.mang-money[data-sign="positive"] { color: var(--m-success); }
|
|
261
|
+
.mang-money[data-sign="negative"] { color: var(--m-danger); }
|
|
262
|
+
|
|
263
|
+
@media (prefers-reduced-motion: reduce) {
|
|
264
|
+
.mang-skeleton, .mang-spinner, .mang-progress[data-indeterminate="true"] .mang-progress-bar { animation: none; }
|
|
265
|
+
.mang-overlay-backdrop, .mang-dialog, .mang-sheet, .mang-toast { animation: none; }
|
|
266
|
+
.mang-btn, .mang-card, .mang-control, .mang-tab { transition: none; }
|
|
267
|
+
}
|
|
268
|
+
`
|
|
269
|
+
);
|
|
270
|
+
function useKitStyles() {
|
|
271
|
+
useScopedStyle(KIT_STYLE_ID, KIT_CSS);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/theme/scope.ts
|
|
275
|
+
function useAreaStyles(id, css) {
|
|
276
|
+
useKitStyles();
|
|
277
|
+
useScopedStyle(id, css);
|
|
278
|
+
}
|
|
279
|
+
function cx(...parts) {
|
|
280
|
+
return parts.filter(Boolean).join(" ");
|
|
281
|
+
}
|
|
282
|
+
function useScope(theme) {
|
|
283
|
+
const resolved = useResolvedTheme(theme);
|
|
284
|
+
const density = useDensity();
|
|
285
|
+
return { className: "mang-ui-scope", "data-theme": resolved, "data-density": density };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export {
|
|
289
|
+
mountScopedStyle,
|
|
290
|
+
MangThemeProvider,
|
|
291
|
+
useMangTheme,
|
|
292
|
+
useResolvedTheme,
|
|
293
|
+
useLocale,
|
|
294
|
+
useDensity,
|
|
295
|
+
useScopedStyle,
|
|
296
|
+
KIT_STYLE_ID,
|
|
297
|
+
useKitStyles,
|
|
298
|
+
useAreaStyles,
|
|
299
|
+
cx,
|
|
300
|
+
useScope
|
|
301
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Badge
|
|
3
|
+
} from "./chunk-FZRXVRC7.js";
|
|
4
|
+
import {
|
|
5
|
+
cx,
|
|
6
|
+
useAreaStyles,
|
|
7
|
+
useScope
|
|
8
|
+
} from "./chunk-3AL4SUFD.js";
|
|
9
|
+
import {
|
|
10
|
+
tokens
|
|
11
|
+
} from "./chunk-PPOYMKV3.js";
|
|
12
|
+
|
|
13
|
+
// src/learning/Learning.tsx
|
|
14
|
+
import { useState } from "react";
|
|
15
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
var STYLE_ID = "mang-ui-learning";
|
|
17
|
+
var { fontSize, fontWeight, radius, font, motion } = tokens;
|
|
18
|
+
var CSS = (
|
|
19
|
+
/* css */
|
|
20
|
+
`
|
|
21
|
+
.mang-flash { position: relative; width: 100%; min-height: 9rem; perspective: 1000px; cursor: pointer; background: none; border: none; padding: 0; font: inherit; text-align: inherit; }
|
|
22
|
+
.mang-flash-inner { position: relative; width: 100%; min-height: 9rem; transition: transform ${motion.duration.slow} ${motion.ease.standard}; transform-style: preserve-3d; }
|
|
23
|
+
.mang-flash[data-flipped="true"] .mang-flash-inner { transform: rotateY(180deg); }
|
|
24
|
+
.mang-flash-face { position: absolute; inset: 0; display: grid; place-items: center; padding: 1.25rem; border: 1px solid var(--m-line); border-radius: ${radius.lg}; background: var(--m-surface); color: var(--m-text); backface-visibility: hidden; text-align: center; }
|
|
25
|
+
.mang-flash-back { transform: rotateY(180deg); background: var(--m-accent-soft); }
|
|
26
|
+
.mang-flash-hint { position: absolute; bottom: 0.5rem; right: 0.7rem; font-size: ${fontSize.xs}; color: var(--m-text-muted); }
|
|
27
|
+
.mang-quiz { display: grid; gap: 0.7rem; padding: 1rem 1.1rem; border: 1px solid var(--m-line); border-radius: ${radius.lg}; background: var(--m-surface); }
|
|
28
|
+
.mang-quiz-q { font-family: "${font.heading}", system-ui, sans-serif; font-weight: ${fontWeight.bold}; font-size: ${fontSize.lg}; color: var(--m-text-strong); }
|
|
29
|
+
.mang-quiz-opts { display: grid; gap: 0.5rem; }
|
|
30
|
+
.mang-quiz-opt { appearance: none; cursor: pointer; text-align: left; display: flex; align-items: center; gap: 0.5rem; padding: 0.7rem 0.85rem; border: 1.5px solid var(--m-line-strong); border-radius: ${radius.md}; background: var(--m-surface); color: var(--m-text); font: inherit; font-size: ${fontSize.sm}; transition: border-color 140ms, background 140ms; }
|
|
31
|
+
.mang-quiz-opt:hover:not(:disabled) { border-color: var(--m-accent); }
|
|
32
|
+
.mang-quiz-opt[data-state="selected"] { border-color: var(--m-accent); background: var(--m-accent-soft); }
|
|
33
|
+
.mang-quiz-opt[data-state="correct"] { border-color: var(--m-success); background: var(--m-success-soft); color: var(--m-success-on); }
|
|
34
|
+
.mang-quiz-opt[data-state="wrong"] { border-color: var(--m-danger); background: var(--m-danger-soft); color: var(--m-danger-on); }
|
|
35
|
+
.mang-quiz-opt:disabled { cursor: default; }
|
|
36
|
+
.mang-quiz-opt:focus-visible { outline: 3px solid color-mix(in oklab, ${tokens.color.mangGreen} 45%, transparent); outline-offset: 2px; }
|
|
37
|
+
.mang-score { display: grid; gap: 0.25rem; justify-items: center; text-align: center; padding: 1.25rem; border: 1px solid var(--m-line); border-radius: ${radius.lg}; background: var(--m-surface); }
|
|
38
|
+
.mang-score-value { font-family: "${font.heading}", system-ui, sans-serif; font-weight: ${fontWeight.bold}; font-size: ${fontSize["3xl"]}; color: var(--m-accent); font-variant-numeric: tabular-nums; }
|
|
39
|
+
.mang-score-label { font-size: ${fontSize.sm}; color: var(--m-text-muted); }
|
|
40
|
+
`
|
|
41
|
+
);
|
|
42
|
+
function Flashcard({
|
|
43
|
+
front,
|
|
44
|
+
back,
|
|
45
|
+
flipped,
|
|
46
|
+
onFlip,
|
|
47
|
+
locale = "vi",
|
|
48
|
+
theme,
|
|
49
|
+
className
|
|
50
|
+
}) {
|
|
51
|
+
useAreaStyles(STYLE_ID, CSS);
|
|
52
|
+
const scope = useScope(theme);
|
|
53
|
+
const [internal, setInternal] = useState(false);
|
|
54
|
+
const isFlipped = flipped ?? internal;
|
|
55
|
+
const toggle = () => {
|
|
56
|
+
const next = !isFlipped;
|
|
57
|
+
if (flipped === void 0) setInternal(next);
|
|
58
|
+
onFlip?.(next);
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
61
|
+
"button",
|
|
62
|
+
{
|
|
63
|
+
type: "button",
|
|
64
|
+
...scope,
|
|
65
|
+
className: cx(scope.className, "mang-flash", className),
|
|
66
|
+
"data-flipped": isFlipped ? "true" : "false",
|
|
67
|
+
onClick: toggle,
|
|
68
|
+
"aria-label": locale === "vi" ? "L\u1EADt th\u1EBB" : "Flip card",
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ jsxs("span", { className: "mang-flash-inner", children: [
|
|
71
|
+
/* @__PURE__ */ jsx("span", { className: "mang-flash-face", children: front }),
|
|
72
|
+
/* @__PURE__ */ jsx("span", { className: "mang-flash-face mang-flash-back", children: back })
|
|
73
|
+
] }),
|
|
74
|
+
/* @__PURE__ */ jsx("span", { className: "mang-flash-hint", children: locale === "vi" ? "b\u1EA5m \u0111\u1EC3 l\u1EADt" : "tap to flip" })
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function QuizCard({
|
|
80
|
+
question,
|
|
81
|
+
options,
|
|
82
|
+
value,
|
|
83
|
+
onSelect,
|
|
84
|
+
correctId,
|
|
85
|
+
revealed,
|
|
86
|
+
theme,
|
|
87
|
+
className
|
|
88
|
+
}) {
|
|
89
|
+
useAreaStyles(STYLE_ID, CSS);
|
|
90
|
+
const scope = useScope(theme);
|
|
91
|
+
const stateOf = (id) => {
|
|
92
|
+
if (revealed && correctId) {
|
|
93
|
+
if (id === correctId) return "correct";
|
|
94
|
+
if (id === value) return "wrong";
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
return id === value ? "selected" : void 0;
|
|
98
|
+
};
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", { ...scope, className: cx(scope.className, "mang-quiz", className), children: [
|
|
100
|
+
/* @__PURE__ */ jsx("div", { className: "mang-quiz-q", children: question }),
|
|
101
|
+
/* @__PURE__ */ jsx("div", { className: "mang-quiz-opts", ...{ role: "radiogroup" }, children: options.map((o) => /* @__PURE__ */ jsx(
|
|
102
|
+
"button",
|
|
103
|
+
{
|
|
104
|
+
type: "button",
|
|
105
|
+
...{ role: "radio" },
|
|
106
|
+
"aria-checked": value === o.id,
|
|
107
|
+
className: "mang-quiz-opt",
|
|
108
|
+
"data-state": stateOf(o.id),
|
|
109
|
+
disabled: revealed,
|
|
110
|
+
onClick: () => onSelect?.(o.id),
|
|
111
|
+
children: o.label
|
|
112
|
+
},
|
|
113
|
+
o.id
|
|
114
|
+
)) })
|
|
115
|
+
] });
|
|
116
|
+
}
|
|
117
|
+
function ScoreCard({ score, total, label, theme, className }) {
|
|
118
|
+
useAreaStyles(STYLE_ID, CSS);
|
|
119
|
+
const scope = useScope(theme);
|
|
120
|
+
return /* @__PURE__ */ jsxs("div", { ...scope, className: cx(scope.className, "mang-score", className), children: [
|
|
121
|
+
/* @__PURE__ */ jsxs("span", { className: "mang-score-value", children: [
|
|
122
|
+
score,
|
|
123
|
+
"/",
|
|
124
|
+
total
|
|
125
|
+
] }),
|
|
126
|
+
label != null && /* @__PURE__ */ jsx("span", { className: "mang-score-label", children: label })
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
function StreakBadge({ days, locale = "vi", theme, className }) {
|
|
130
|
+
return /* @__PURE__ */ jsxs(Badge, { tone: "warning", icon: "\u{1F525}", theme, className, children: [
|
|
131
|
+
days,
|
|
132
|
+
" ",
|
|
133
|
+
locale === "vi" ? "ng\xE0y" : "days"
|
|
134
|
+
] });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
Flashcard,
|
|
139
|
+
QuizCard,
|
|
140
|
+
ScoreCard,
|
|
141
|
+
StreakBadge
|
|
142
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cx,
|
|
3
|
+
useKitStyles,
|
|
4
|
+
useScope
|
|
5
|
+
} from "./chunk-3AL4SUFD.js";
|
|
6
|
+
|
|
7
|
+
// src/surface/Card.tsx
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
function Card({ interactive, theme, className, children, ...rest }) {
|
|
10
|
+
useKitStyles();
|
|
11
|
+
const scope = useScope(theme);
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
"div",
|
|
14
|
+
{
|
|
15
|
+
...rest,
|
|
16
|
+
...scope,
|
|
17
|
+
className: cx(scope.className, "mang-card", className),
|
|
18
|
+
"data-interactive": interactive ? "true" : void 0,
|
|
19
|
+
children
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
var sub = (cls) => ({
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
style
|
|
27
|
+
}) => /* @__PURE__ */ jsx("div", { className: cx(cls, className), style, children });
|
|
28
|
+
var CardHeader = sub("mang-card-head");
|
|
29
|
+
var CardBody = sub("mang-card-body");
|
|
30
|
+
var CardFooter = sub("mang-card-foot");
|
|
31
|
+
function Panel({ theme, className, children, ...rest }) {
|
|
32
|
+
useKitStyles();
|
|
33
|
+
const scope = useScope(theme);
|
|
34
|
+
return /* @__PURE__ */ jsx("div", { ...rest, ...scope, className: cx(scope.className, "mang-panel", className), children });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
Card,
|
|
39
|
+
CardHeader,
|
|
40
|
+
CardBody,
|
|
41
|
+
CardFooter,
|
|
42
|
+
Panel
|
|
43
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToastProvider
|
|
3
|
+
} from "./chunk-DTASXPTB.js";
|
|
4
|
+
import {
|
|
5
|
+
MangThemeProvider,
|
|
6
|
+
cx,
|
|
7
|
+
useKitStyles,
|
|
8
|
+
useScope
|
|
9
|
+
} from "./chunk-3AL4SUFD.js";
|
|
10
|
+
import {
|
|
11
|
+
defaultTheme
|
|
12
|
+
} from "./chunk-PPOYMKV3.js";
|
|
13
|
+
|
|
14
|
+
// src/theme/applyThemeVars.ts
|
|
15
|
+
function applyThemeVars(el, theme = defaultTheme) {
|
|
16
|
+
for (const [name, value] of Object.entries(theme.color)) {
|
|
17
|
+
el.style.setProperty(`--mang-color-${name}`, value);
|
|
18
|
+
}
|
|
19
|
+
el.style.setProperty("--mang-font-heading", `"${theme.font.heading}"`);
|
|
20
|
+
el.style.setProperty("--mang-font-body", `"${theme.font.body}"`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/app/MangApp.tsx
|
|
24
|
+
import { useEffect, useRef } from "react";
|
|
25
|
+
import { jsx } from "react/jsx-runtime";
|
|
26
|
+
function MangApp({
|
|
27
|
+
theme = "light",
|
|
28
|
+
locale = "vi",
|
|
29
|
+
density = "comfortable",
|
|
30
|
+
sdkTheme,
|
|
31
|
+
withToasts = true,
|
|
32
|
+
className,
|
|
33
|
+
style,
|
|
34
|
+
children
|
|
35
|
+
}) {
|
|
36
|
+
useKitStyles();
|
|
37
|
+
const scope = useScope(theme);
|
|
38
|
+
const ref = useRef(null);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (sdkTheme && ref.current) applyThemeVars(ref.current, sdkTheme);
|
|
41
|
+
}, [sdkTheme]);
|
|
42
|
+
const root = /* @__PURE__ */ jsx(
|
|
43
|
+
"div",
|
|
44
|
+
{
|
|
45
|
+
ref,
|
|
46
|
+
...scope,
|
|
47
|
+
"data-density": density,
|
|
48
|
+
className: cx(scope.className, className),
|
|
49
|
+
style,
|
|
50
|
+
children
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
return /* @__PURE__ */ jsx(MangThemeProvider, { theme, locale, density, children: withToasts ? /* @__PURE__ */ jsx(ToastProvider, { theme, children: root }) : root });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
applyThemeVars,
|
|
58
|
+
MangApp
|
|
59
|
+
};
|