@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.
Files changed (95) hide show
  1. package/LICENSE +26 -0
  2. package/dist/MangThemeProvider-BqdGKBP2.d.ts +34 -0
  3. package/dist/Money-Dw3GnUvX.d.ts +27 -0
  4. package/dist/actions.d.ts +31 -0
  5. package/dist/actions.js +13 -0
  6. package/dist/ai.d.ts +58 -0
  7. package/dist/ai.js +13 -0
  8. package/dist/analytics.d.ts +40 -0
  9. package/dist/analytics.js +15 -0
  10. package/dist/app.d.ts +24 -0
  11. package/dist/app.js +9 -0
  12. package/dist/catalog.d.ts +76 -0
  13. package/dist/catalog.js +1210 -0
  14. package/dist/charts.d.ts +89 -0
  15. package/dist/charts.js +34 -0
  16. package/dist/chunk-3AL4SUFD.js +301 -0
  17. package/dist/chunk-4XNSYKQE.js +142 -0
  18. package/dist/chunk-5Z4VLQKH.js +43 -0
  19. package/dist/chunk-7P2EQZYD.js +59 -0
  20. package/dist/chunk-7WHNIEDV.js +120 -0
  21. package/dist/chunk-ASZKHSMG.js +82 -0
  22. package/dist/chunk-BCBN2EGH.js +216 -0
  23. package/dist/chunk-BLYAFV45.js +320 -0
  24. package/dist/chunk-DLKEXWPA.js +90 -0
  25. package/dist/chunk-DTASXPTB.js +70 -0
  26. package/dist/chunk-FZRXVRC7.js +63 -0
  27. package/dist/chunk-ID233AGM.js +108 -0
  28. package/dist/chunk-IVYXOKMO.js +74 -0
  29. package/dist/chunk-IX3DYETF.js +61 -0
  30. package/dist/chunk-JJB4PJC3.js +166 -0
  31. package/dist/chunk-K5Q3RCV6.js +119 -0
  32. package/dist/chunk-LNRUPJDF.js +161 -0
  33. package/dist/chunk-LZORNMBL.js +0 -0
  34. package/dist/chunk-OBPXCUVF.js +282 -0
  35. package/dist/chunk-OJX2EIMB.js +145 -0
  36. package/dist/chunk-PPOYMKV3.js +170 -0
  37. package/dist/chunk-PQGUWJG4.js +47 -0
  38. package/dist/chunk-RE7OWRA4.js +187 -0
  39. package/dist/chunk-SJF3CHAW.js +108 -0
  40. package/dist/chunk-UF6ANDJZ.js +112 -0
  41. package/dist/chunk-VGC5DMOM.js +107 -0
  42. package/dist/chunk-VP56Z4BS.js +0 -0
  43. package/dist/chunk-VRD66FIA.js +77 -0
  44. package/dist/chunk-X7T2DJLU.js +113 -0
  45. package/dist/chunk-XPV3OOLU.js +147 -0
  46. package/dist/chunk-YN5O6YL6.js +69 -0
  47. package/dist/chunk-Z4ANGBPC.js +94 -0
  48. package/dist/creator.d.ts +55 -0
  49. package/dist/creator.js +20 -0
  50. package/dist/data-room.d.ts +50 -0
  51. package/dist/data-room.js +17 -0
  52. package/dist/editor.d.ts +32 -0
  53. package/dist/editor.js +14 -0
  54. package/dist/feedback.d.ts +48 -0
  55. package/dist/feedback.js +16 -0
  56. package/dist/forms.d.ts +91 -0
  57. package/dist/forms.js +26 -0
  58. package/dist/handoff.d.ts +37 -0
  59. package/dist/handoff.js +13 -0
  60. package/dist/index.css +2 -0
  61. package/dist/index.d.ts +62 -0
  62. package/dist/index.js +338 -0
  63. package/dist/layout.d.ts +57 -0
  64. package/dist/layout.js +22 -0
  65. package/dist/learning.d.ts +46 -0
  66. package/dist/learning.js +15 -0
  67. package/dist/media.d.ts +48 -0
  68. package/dist/media.js +16 -0
  69. package/dist/monetization.d.ts +30 -0
  70. package/dist/monetization.js +14 -0
  71. package/dist/money.d.ts +45 -0
  72. package/dist/money.js +28 -0
  73. package/dist/navigation.d.ts +36 -0
  74. package/dist/navigation.js +14 -0
  75. package/dist/overlay.d.ts +72 -0
  76. package/dist/overlay.js +20 -0
  77. package/dist/platform.d.ts +94 -0
  78. package/dist/platform.js +42 -0
  79. package/dist/primitives.d.ts +83 -0
  80. package/dist/primitives.js +22 -0
  81. package/dist/privacy.d.ts +28 -0
  82. package/dist/privacy.js +15 -0
  83. package/dist/sandbox.d.ts +40 -0
  84. package/dist/sandbox.js +15 -0
  85. package/dist/settings.d.ts +29 -0
  86. package/dist/settings.js +13 -0
  87. package/dist/surface.d.ts +33 -0
  88. package/dist/surface.js +16 -0
  89. package/dist/theme.css +63 -0
  90. package/dist/theme.d.ts +64 -0
  91. package/dist/theme.js +27 -0
  92. package/dist/tokens.css +119 -0
  93. package/dist/tokens.d.ts +128 -0
  94. package/dist/tokens.js +8 -0
  95. package/package.json +151 -0
@@ -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
+ };