@madojs/mado 0.10.0 → 0.11.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/AGENTS.md +24 -26
- package/CHANGELOG.md +98 -0
- package/README.md +18 -45
- package/TODO.md +52 -48
- package/dist/src/component.d.ts +2 -1
- package/dist/src/component.js +5 -2
- package/dist/src/component.js.map +1 -1
- package/dist/src/each.d.ts +1 -1
- package/dist/src/each.js +1 -1
- package/dist/src/each.js.map +1 -1
- package/dist/src/html/template.js +10 -0
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +11 -6
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lazy.d.ts +1 -1
- package/dist/src/lazy.js +1 -1
- package/dist/src/lazy.js.map +1 -1
- package/dist/src/page.d.ts +17 -21
- package/dist/src/page.js +7 -12
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +1 -1
- package/dist/src/router/manifest.js +21 -13
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +2 -2
- package/dist/src/router/match.js +3 -3
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +1 -1
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/vite/index.d.ts +10 -0
- package/dist/src/vite/index.js +33 -0
- package/dist/src/vite/index.js.map +1 -0
- package/docs/en/00-the-mado-way.md +25 -12
- package/docs/en/01-routing.md +90 -142
- package/docs/en/02-project-layout.md +59 -52
- package/docs/en/03-static-bake.md +5 -6
- package/docs/en/05-why-mado.md +6 -6
- package/docs/en/06-for-backenders.md +18 -22
- package/docs/en/08-llm-zero-history-test.md +9 -14
- package/docs/en/09-shadow-vs-light-dom.md +28 -36
- package/docs/en/10-app-architecture.md +158 -96
- package/docs/en/11-layouts.md +22 -24
- package/docs/en/12-auth-and-api.md +89 -182
- package/docs/en/13-deployment.md +25 -26
- package/docs/en/14-testing.md +4 -4
- package/docs/en/16-bake-cookbook.md +17 -10
- package/docs/en/18-api-freeze-map.md +6 -4
- package/docs/en/20-v1-stability.md +1 -1
- package/docs/fr/00-the-mado-way.md +55 -90
- package/docs/fr/01-routing.md +70 -152
- package/docs/fr/02-project-layout.md +74 -48
- package/docs/fr/03-static-bake.md +1 -1
- package/docs/fr/05-why-mado.md +6 -6
- package/docs/fr/06-for-backenders.md +7 -7
- package/docs/fr/08-llm-zero-history-test.md +21 -48
- package/docs/fr/09-shadow-vs-light-dom.md +43 -162
- package/docs/fr/10-app-architecture.md +110 -33
- package/docs/fr/11-layouts.md +24 -12
- package/docs/fr/12-auth-and-api.md +63 -22
- package/docs/fr/13-deployment.md +30 -12
- package/docs/fr/14-testing.md +1 -1
- package/docs/fr/16-bake-cookbook.md +57 -4
- package/docs/fr/18-api-freeze-map.md +1 -1
- package/docs/fr/20-v1-stability.md +1 -1
- package/docs/recipes/nginx/README.md +13 -0
- package/docs/ru/00-the-mado-way.md +53 -75
- package/docs/ru/01-routing.md +68 -143
- package/docs/ru/02-project-layout.md +75 -48
- package/docs/ru/03-static-bake.md +2 -2
- package/docs/ru/05-why-mado.md +6 -6
- package/docs/ru/06-for-backenders.md +7 -7
- package/docs/ru/08-llm-zero-history-test.md +9 -14
- package/docs/ru/09-shadow-vs-light-dom.md +43 -178
- package/docs/ru/10-app-architecture.md +115 -63
- package/docs/ru/11-layouts.md +24 -24
- package/docs/ru/12-auth-and-api.md +57 -35
- package/docs/ru/13-deployment.md +19 -13
- package/docs/ru/14-testing.md +1 -1
- package/docs/ru/16-bake-cookbook.md +48 -8
- package/docs/ru/18-api-freeze-map.md +5 -3
- package/docs/ru/20-v1-stability.md +1 -1
- package/docs/uk/00-the-mado-way.md +70 -44
- package/docs/uk/01-routing.md +41 -47
- package/docs/uk/02-project-layout.md +68 -41
- package/docs/uk/03-static-bake.md +1 -2
- package/docs/uk/06-for-backenders.md +3 -3
- package/docs/uk/08-llm-zero-history-test.md +22 -24
- package/docs/uk/09-shadow-vs-light-dom.md +37 -86
- package/docs/uk/10-app-architecture.md +72 -31
- package/docs/uk/11-layouts.md +25 -12
- package/docs/uk/12-auth-and-api.md +58 -22
- package/docs/uk/13-deployment.md +4 -3
- package/docs/uk/14-testing.md +1 -1
- package/docs/uk/18-api-freeze-map.md +1 -1
- package/docs/uk/20-v1-stability.md +1 -1
- package/llms.txt +14 -15
- package/package.json +18 -11
- package/scripts/_config.mjs +15 -161
- package/scripts/bake.mjs +71 -58
- package/scripts/cli/generate.mjs +348 -0
- package/scripts/cli/help.mjs +27 -0
- package/scripts/cli/index.mjs +79 -0
- package/scripts/cli/init.mjs +153 -0
- package/scripts/cli/release.mjs +152 -0
- package/scripts/cli/run.mjs +96 -0
- package/scripts/cli.mjs +2 -560
- package/scripts/package-smoke.mjs +4 -1
- package/scripts/preview.mjs +17 -61
- package/scripts/size-budget.mjs +5 -2
- package/scripts/vite.default.mjs +11 -0
- package/starters/default/.editorconfig +12 -0
- package/starters/default/README.md +74 -0
- package/starters/default/eslint.config.mjs +256 -0
- package/starters/default/index.html +13 -0
- package/starters/default/package.json +30 -0
- package/starters/default/public/favicon.svg +4 -0
- package/starters/default/src/app.routes.ts +39 -0
- package/starters/default/src/layouts/app-shell.layout.ts +35 -0
- package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
- package/starters/default/src/main.ts +16 -0
- package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
- package/starters/default/src/modules/auth/auth.connector.ts +45 -0
- package/starters/default/src/modules/auth/auth.guard.ts +22 -0
- package/starters/default/src/modules/auth/auth.public.ts +9 -0
- package/starters/default/src/modules/auth/auth.routes.ts +8 -0
- package/starters/default/src/modules/auth/auth.service.ts +71 -0
- package/starters/default/src/modules/auth/auth.types.ts +15 -0
- package/starters/default/src/modules/auth/login.page.ts +62 -0
- package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
- package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
- package/starters/default/src/modules/billing/billing.public.ts +5 -0
- package/starters/default/src/modules/billing/billing.routes.ts +9 -0
- package/starters/default/src/modules/billing/billing.types.ts +15 -0
- package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
- package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
- package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
- package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
- package/starters/default/src/modules/home/home.page.ts +34 -0
- package/starters/default/src/modules/home/not-found.page.ts +11 -0
- package/starters/default/src/shared/http/http-client.ts +86 -0
- package/starters/default/src/shared/http/http-error.ts +37 -0
- package/starters/default/src/shared/http/interceptors.ts +59 -0
- package/starters/default/src/shared/lib/format-date.ts +19 -0
- package/starters/default/src/shared/styles/content.css +70 -0
- package/starters/default/src/shared/styles/reset.css +32 -0
- package/starters/default/src/shared/styles/shell.css +57 -0
- package/starters/default/src/shared/styles/tokens.css +44 -0
- package/starters/default/src/shared/ui/x-button.component.ts +49 -0
- package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
- package/starters/default/src/styles.d.ts +1 -0
- package/starters/default/src/vite-env.d.ts +1 -0
- package/starters/default/tsconfig.json +24 -0
- package/starters/default/vite.config.ts +9 -0
- package/MADO_V1_PLAN.md +0 -179
- package/ROADMAP.md +0 -178
- package/dist/src/html.d.ts +0 -18
- package/dist/src/html.js +0 -17
- package/dist/src/html.js.map +0 -1
- package/dist/src/router.d.ts +0 -13
- package/dist/src/router.js +0 -13
- package/dist/src/router.js.map +0 -1
- package/scripts/bundle.mjs +0 -212
- package/scripts/llm-zero-history-smoke.mjs +0 -93
- package/scripts/new.mjs +0 -80
- package/scripts/showcase-regression.mjs +0 -392
- package/server/serve.mjs +0 -455
- package/starters/admin/README.md +0 -63
- package/starters/admin/index.html +0 -28
- package/starters/admin/mado.config.json +0 -22
- package/starters/admin/package.json +0 -24
- package/starters/admin/public/favicon.svg +0 -4
- package/starters/admin/src/components/x-button.ts +0 -82
- package/starters/admin/src/components/x-input.ts +0 -105
- package/starters/admin/src/layouts/app.ts +0 -101
- package/starters/admin/src/layouts/auth.ts +0 -41
- package/starters/admin/src/lib/api.ts +0 -184
- package/starters/admin/src/lib/auth.ts +0 -83
- package/starters/admin/src/main.ts +0 -15
- package/starters/admin/src/pages/admin/dashboard.ts +0 -48
- package/starters/admin/src/pages/admin/order-detail.ts +0 -80
- package/starters/admin/src/pages/admin/orders.ts +0 -117
- package/starters/admin/src/pages/home.ts +0 -34
- package/starters/admin/src/pages/login.ts +0 -70
- package/starters/admin/src/pages/not-found.ts +0 -12
- package/starters/admin/src/routes.ts +0 -40
- package/starters/admin/src/styles/global.ts +0 -86
- package/starters/admin/tsconfig.json +0 -15
- package/starters/crud/README.md +0 -33
- package/starters/crud/index.html +0 -28
- package/starters/crud/mado.config.json +0 -20
- package/starters/crud/package.json +0 -24
- package/starters/crud/src/components/app-shell.ts +0 -56
- package/starters/crud/src/components/ticket-detail.ts +0 -33
- package/starters/crud/src/components/ticket-form.ts +0 -69
- package/starters/crud/src/components/ticket-list.ts +0 -66
- package/starters/crud/src/lib/api.ts +0 -76
- package/starters/crud/src/main.ts +0 -9
- package/starters/crud/src/pages/home.ts +0 -34
- package/starters/crud/src/pages/not-found.ts +0 -12
- package/starters/crud/src/pages/ticket-detail.ts +0 -7
- package/starters/crud/src/pages/ticket-new.ts +0 -7
- package/starters/crud/src/pages/tickets.ts +0 -7
- package/starters/crud/src/routes.ts +0 -11
- package/starters/crud/src/styles/global.ts +0 -155
- package/starters/crud/tsconfig.json +0 -15
- package/starters/minimal/README.md +0 -21
- package/starters/minimal/index.html +0 -28
- package/starters/minimal/mado.config.json +0 -20
- package/starters/minimal/package.json +0 -24
- package/starters/minimal/src/components/app-counter.ts +0 -31
- package/starters/minimal/src/main.ts +0 -9
- package/starters/minimal/src/pages/home.ts +0 -35
- package/starters/minimal/src/pages/not-found.ts +0 -14
- package/starters/minimal/src/routes.ts +0 -8
- package/starters/minimal/src/styles/global.ts +0 -60
- package/starters/minimal/tsconfig.json +0 -15
- package/templates/page-detail.ts +0 -63
- package/templates/page-form.ts +0 -94
- package/templates/page-list.ts +0 -79
|
@@ -1,198 +1,63 @@
|
|
|
1
1
|
# Shadow DOM vs Light DOM
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
самодостаточных виджетов, но
|
|
3
|
+
Mado components используют Shadow DOM по умолчанию. Это хороший default для
|
|
4
|
+
самодостаточных виджетов, но app zones и страницы обычно должны оставаться
|
|
5
|
+
простыми light DOM templates.
|
|
5
6
|
|
|
6
|
-
## Правило
|
|
7
|
+
## Правило
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
часть UI-дерева — app shell, sidebar, modal, table, page section — по умолчанию
|
|
10
|
-
делайте Web Component через `component()`.
|
|
11
|
-
|
|
12
|
-
Обычные функции оставляйте для маленьких inline helpers:
|
|
9
|
+
Используйте route layouts для app zones:
|
|
13
10
|
|
|
14
11
|
```ts
|
|
15
|
-
|
|
12
|
+
export default page({
|
|
13
|
+
view: ({ child }) => html`<main class="app-main">${child}</main>`,
|
|
14
|
+
});
|
|
16
15
|
```
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Используйте **Shadow DOM** для leaf-виджетов:
|
|
22
|
-
|
|
23
|
-
- кнопки, бейджи, карточки, метрики;
|
|
24
|
-
- модалы, тосты, маленькие визуальные компоненты;
|
|
25
|
-
- embed-виджеты, которые не должны наследовать CSS приложения случайно;
|
|
26
|
-
- компоненты, стили которых принадлежат самому компоненту.
|
|
27
|
-
|
|
28
|
-
Используйте **Light DOM** (`{ shadow: false }`) для структуры приложения,
|
|
29
|
-
которая хочет разделять глобальные CSS-утилиты:
|
|
30
|
-
|
|
31
|
-
- route/page компоненты;
|
|
32
|
-
- admin-экраны с плотными таблицами/формами;
|
|
33
|
-
- data-heavy экраны с таблицами и формами;
|
|
34
|
-
- компоненты, которые намеренно используют глобальные layout/form/table утилиты;
|
|
35
|
-
- места, где children должны оставаться обычным document DOM.
|
|
36
|
-
|
|
37
|
-
Используйте **Shadow DOM** для slot-based layouts:
|
|
38
|
-
|
|
39
|
-
- app shells с `<slot>`;
|
|
40
|
-
- sidebar/content wrappers;
|
|
41
|
-
- reusable layout frames, которые владеют своим grid/header/sidebar CSS.
|
|
42
|
-
|
|
43
|
-
`<slot>` — это feature Shadow DOM. В компоненте с `shadow: false` тег `<slot>`
|
|
44
|
-
становится обычным DOM-элементом и не переносит children в это место layout.
|
|
45
|
-
|
|
46
|
-
## Подвох
|
|
47
|
-
|
|
48
|
-
Глобальный CSS не пересекает границу Shadow DOM.
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
// global.ts
|
|
52
|
-
export const globalStyles = css`
|
|
53
|
-
.page-head {
|
|
54
|
-
display: flex;
|
|
55
|
-
justify-content: space-between;
|
|
56
|
-
}
|
|
57
|
-
.metric-grid {
|
|
58
|
-
display: grid;
|
|
59
|
-
grid-template-columns: repeat(4, 1fr);
|
|
60
|
-
}
|
|
61
|
-
`;
|
|
62
|
-
|
|
63
|
-
// ❌ .page-head и .metric-grid НЕ применятся внутри shadowRoot x-dashboard
|
|
64
|
-
component(
|
|
65
|
-
"x-dashboard",
|
|
66
|
-
() => () => html`
|
|
67
|
-
<header class="page-head">...</header>
|
|
68
|
-
<div class="metric-grid">...</div>
|
|
69
|
-
`,
|
|
70
|
-
);
|
|
71
|
-
```
|
|
17
|
+
Такие файлы живут в `src/layouts/` и подключаются в `src/app.routes.ts` через
|
|
18
|
+
`layout()`. Они стилизуются обычным CSS из `src/shared/styles/shell.css`.
|
|
72
19
|
|
|
73
|
-
|
|
20
|
+
Используйте page files для screens:
|
|
74
21
|
|
|
75
22
|
```ts
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<header class="page-head">...</header>
|
|
80
|
-
<div class="metric-grid">...</div>
|
|
81
|
-
`,
|
|
82
|
-
{
|
|
83
|
-
shadow: false,
|
|
84
|
-
styles: css`
|
|
85
|
-
x-dashboard {
|
|
86
|
-
display: block;
|
|
87
|
-
}
|
|
88
|
-
x-dashboard .panel {
|
|
89
|
-
padding: 1rem;
|
|
90
|
-
}
|
|
91
|
-
`,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
23
|
+
export default page({
|
|
24
|
+
view: () => html`<section><h1>Users</h1></section>`,
|
|
25
|
+
});
|
|
94
26
|
```
|
|
95
27
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
## Как ведут себя стили
|
|
28
|
+
Page-level tables, forms, prose and simple states стилизуются из
|
|
29
|
+
`src/shared/styles/content.css`.
|
|
99
30
|
|
|
100
|
-
|
|
101
|
-
- `styles: css\`\``с`shadow: false` скоупится по имени тега и адоптируется
|
|
102
|
-
глобально.
|
|
103
|
-
- CSS custom properties (`--accent`, `--bg` и т.д.) пересекают границу Shadow DOM.
|
|
104
|
-
- Селекторы по классу (`.btn`, `.form-grid`, `.page-head`) **не** пересекают
|
|
105
|
-
границу Shadow DOM.
|
|
106
|
-
- Slotted-children сохраняют свои стили из документа; shadow-компонент может
|
|
107
|
-
таргетировать их только через `::slotted(...)`.
|
|
108
|
-
- `<slot>` проецирует children только в Shadow DOM. В компоненте с `shadow: false`
|
|
109
|
-
это обычный `<slot>` элемент, который не перемещает children в своё место.
|
|
31
|
+
Используйте Shadow DOM components для leaf widgets:
|
|
110
32
|
|
|
111
|
-
|
|
33
|
+
- buttons, badges, cards, metrics;
|
|
34
|
+
- spinners, modals, toasts;
|
|
35
|
+
- widgets, которые должны владеть своим CSS.
|
|
112
36
|
|
|
113
37
|
```ts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
component("x-stat-card", setup);
|
|
124
|
-
component("x-toast-stack", setup);
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Это даёт backend-admin экранам предсказуемый CSS, сохраняя инкапсуляцию
|
|
128
|
-
для переиспользуемых виджетов и slot-based shells.
|
|
129
|
-
|
|
130
|
-
Import model специально browser-native:
|
|
131
|
-
|
|
132
|
-
```ts
|
|
133
|
-
import "./components/app-layout.js";
|
|
134
|
-
|
|
135
|
-
render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Import регистрирует custom element через `customElements.define()`. Template
|
|
139
|
-
создаёт `<x-app-layout>` элемент. Дальше браузер сам связывает тег с классом
|
|
140
|
-
компонента. Тут нет React-style component value, который передаётся как функция.
|
|
141
|
-
|
|
142
|
-
Если layout не нуждается в slot projection и должен стилизоваться полностью
|
|
143
|
-
глобальным CSS, `shadow: false` — хороший выбор. Если он содержит `<slot>`,
|
|
144
|
-
оставьте Shadow DOM и поместите стили shell в этот компонент.
|
|
145
|
-
|
|
146
|
-
## Маршрутизация и ссылки
|
|
147
|
-
|
|
148
|
-
`data-link` работает внутри Shadow DOM. Роутер использует `event.composedPath()`,
|
|
149
|
-
поэтому перехват кликов и hover-prefetch видят ссылки из open shadow roots.
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
component(
|
|
153
|
-
"x-card-link",
|
|
154
|
-
() => () => html` <a href="/app/accounts" data-link>Accounts</a> `,
|
|
155
|
-
);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Ссылка может быть внутри Shadow DOM — навигация всё равно остаётся SPA.
|
|
159
|
-
|
|
160
|
-
## Где импортировать компоненты
|
|
161
|
-
|
|
162
|
-
Custom elements становятся глобальными после регистрации, но регистрация всё
|
|
163
|
-
равно остаётся явным JavaScript import.
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
// main.ts: global app frame
|
|
167
|
-
import "./components/app-shell.js";
|
|
168
|
-
|
|
169
|
-
// pages/tickets.ts: component, которым владеет эта page
|
|
170
|
-
import "../components/ticket-list.js";
|
|
38
|
+
component("x-status-badge", ({ attr }) => {
|
|
39
|
+
const status = attr("status", "draft");
|
|
40
|
+
return () => html`<span>${status}</span>`;
|
|
41
|
+
}, {
|
|
42
|
+
styles: css`
|
|
43
|
+
:host { display: inline-block; }
|
|
44
|
+
span { color: var(--color-text-muted); }
|
|
45
|
+
`,
|
|
46
|
+
});
|
|
171
47
|
```
|
|
172
48
|
|
|
173
|
-
|
|
174
|
-
`<ticket-list>`. Файл должен быть где-то импортирован. После import он вызывает
|
|
175
|
-
`customElements.define(...)`, и тег становится известен текущему document.
|
|
176
|
-
|
|
177
|
-
Не стоит bulk-import всех компонентов в `main.ts` «на всякий случай». Для маленьких
|
|
178
|
-
demo это работает, но прячет ownership и ломает lazy route loading. Лучше:
|
|
179
|
-
|
|
180
|
-
- global app shell/providers импортировать в `main.ts`;
|
|
181
|
-
- components, которыми владеет одна page, импортировать в этой page;
|
|
182
|
-
- shared components feature-а импортировать в feature entry page;
|
|
183
|
-
- truly global leaf components импортировать в `main.ts` только если они реально
|
|
184
|
-
используются везде.
|
|
185
|
-
|
|
186
|
-
## Урок из showcase
|
|
187
|
-
|
|
188
|
-
`examples/showcase` использует это разделение намеренно:
|
|
189
|
-
|
|
190
|
-
- `x-app` и CRM route pages — Light DOM;
|
|
191
|
-
- `x-app-layout` остаётся в Shadow DOM, потому что владеет slot-based
|
|
192
|
-
sidebar/content shell;
|
|
193
|
-
- table/form/page утилиты живут в `styles/global.ts`;
|
|
194
|
-
- leaf-компоненты типа `x-stat-card`, `x-status-badge`, `x-modal` и
|
|
195
|
-
`x-toast-stack` остаются в Shadow DOM.
|
|
49
|
+
## Как ведут себя стили
|
|
196
50
|
|
|
197
|
-
|
|
198
|
-
|
|
51
|
+
- `tokens.css` задает CSS custom properties; `var(...)` проходит через Shadow
|
|
52
|
+
DOM.
|
|
53
|
+
- `reset.css`, `shell.css`, `content.css` применяются только к document/light
|
|
54
|
+
DOM.
|
|
55
|
+
- Class selectors вроде `.data`, `.app-main`, `.error` не проходят внутрь
|
|
56
|
+
Shadow DOM.
|
|
57
|
+
- Component-local styles пишутся в ``css`...` `` внутри `component()` options.
|
|
58
|
+
- Если Shadow component принимает children, используйте `<slot>` и стилизуйте
|
|
59
|
+
frame внутри component styles.
|
|
60
|
+
|
|
61
|
+
Если page выглядит без стилей, почти всегда вы использовали global classes
|
|
62
|
+
внутри Shadow DOM component. Вынесите markup в page/layout или перенесите CSS в
|
|
63
|
+
component styles.
|
|
@@ -1,100 +1,152 @@
|
|
|
1
1
|
# Архитектура приложения
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Официальный starter — каноничная production-форма Mado-приложения. Это не
|
|
4
|
+
framework внутри framework: только файлы, imports, Mado primitives и ESLint
|
|
5
|
+
boundaries.
|
|
6
6
|
|
|
7
7
|
## Структура
|
|
8
8
|
|
|
9
9
|
```txt
|
|
10
10
|
src/
|
|
11
11
|
├── main.ts
|
|
12
|
-
├── routes.ts
|
|
12
|
+
├── app.routes.ts
|
|
13
13
|
├── layouts/
|
|
14
|
-
│ ├── app.ts
|
|
15
|
-
│ └── auth.ts
|
|
16
|
-
├──
|
|
17
|
-
│ ├──
|
|
18
|
-
│ ├──
|
|
19
|
-
│ ├──
|
|
20
|
-
│ └──
|
|
21
|
-
|
|
22
|
-
├──
|
|
23
|
-
│ ├──
|
|
24
|
-
│
|
|
25
|
-
|
|
14
|
+
│ ├── app-shell.layout.ts
|
|
15
|
+
│ └── auth-shell.layout.ts
|
|
16
|
+
├── shared/
|
|
17
|
+
│ ├── http/
|
|
18
|
+
│ ├── lib/
|
|
19
|
+
│ ├── styles/
|
|
20
|
+
│ └── ui/
|
|
21
|
+
└── modules/
|
|
22
|
+
├── auth/
|
|
23
|
+
│ ├── auth.routes.ts
|
|
24
|
+
│ ├── auth.public.ts
|
|
25
|
+
│ ├── auth.service.ts
|
|
26
|
+
│ ├── auth.connector.ts
|
|
27
|
+
│ ├── auth.guard.ts
|
|
28
|
+
│ ├── login.page.ts
|
|
29
|
+
│ └── _contracts/
|
|
30
|
+
└── billing/
|
|
31
|
+
├── billing.routes.ts
|
|
32
|
+
├── billing.public.ts
|
|
33
|
+
├── billing.types.ts
|
|
34
|
+
├── api/
|
|
35
|
+
├── data/
|
|
36
|
+
├── pages/
|
|
37
|
+
├── components/
|
|
38
|
+
└── _contracts/
|
|
26
39
|
```
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
`components/` хранит переиспользуемые UI-теги, а `pages/` содержит один файл
|
|
30
|
-
на страницу.
|
|
41
|
+
## App Map
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
import { html, render } from "@madojs/mado";
|
|
36
|
-
import "./styles/global.js";
|
|
37
|
-
import "./components/x-button.js";
|
|
38
|
-
import routesApi from "./routes.js";
|
|
39
|
-
|
|
40
|
-
render(html`${routesApi.view}`, document.getElementById("app")!);
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
В `main.ts` импортируй только глобальные провайдеры, стили и маленькие общие
|
|
44
|
-
компоненты. Feature-компоненты импортирует страница, которая их рендерит.
|
|
45
|
-
|
|
46
|
-
## Routes
|
|
43
|
+
`src/app.routes.ts` — карта всего приложения. Modules экспортируют plain route
|
|
44
|
+
maps; app routes решают, какой shell и guard оборачивают каждую зону.
|
|
47
45
|
|
|
48
46
|
```ts
|
|
49
47
|
import { layout, routes } from "@madojs/mado";
|
|
50
|
-
import { requireAuth } from "./
|
|
48
|
+
import { requireAuth } from "./modules/auth/auth.public";
|
|
49
|
+
import { authRoutes } from "./modules/auth/auth.routes";
|
|
50
|
+
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
51
51
|
|
|
52
52
|
export const manifest = {
|
|
53
|
-
"/": () => import("./
|
|
53
|
+
"/": () => import("./modules/home/home.page"),
|
|
54
54
|
"/login": layout({
|
|
55
|
-
layout: () => import("./layouts/auth.
|
|
56
|
-
routes:
|
|
55
|
+
layout: () => import("./layouts/auth-shell.layout"),
|
|
56
|
+
routes: authRoutes,
|
|
57
57
|
}),
|
|
58
|
-
"/
|
|
59
|
-
layout: () => import("./layouts/app.
|
|
58
|
+
"/billing": layout({
|
|
59
|
+
layout: () => import("./layouts/app-shell.layout"),
|
|
60
60
|
guard: requireAuth,
|
|
61
|
-
routes:
|
|
62
|
-
"/": () => import("./pages/admin/dashboard.js"),
|
|
63
|
-
"/orders": () => import("./pages/admin/orders.js"),
|
|
64
|
-
},
|
|
61
|
+
routes: billingRoutes,
|
|
65
62
|
}),
|
|
66
|
-
"*": () => import("./
|
|
63
|
+
"*": () => import("./modules/home/not-found.page"),
|
|
67
64
|
};
|
|
68
65
|
|
|
69
66
|
export default routes(manifest);
|
|
70
67
|
```
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
Rules:
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
- Export `manifest`, чтобы `mado bake` мог найти bakeable pages.
|
|
72
|
+
- Modules не вызывают `layout()`.
|
|
73
|
+
- Layouts описывают app zones, не домены.
|
|
74
|
+
- Router не прячется в custom element или втором shell в `main.ts`.
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
для записей `mutation(..., { invalidates })`, для форм `useForm()`.
|
|
76
|
+
## File Forms
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
| Suffix | Role |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `*.page.ts` | route page, default `page({...})` |
|
|
81
|
+
| `*.layout.ts` | app-zone wrapper, default `page(...)` |
|
|
82
|
+
| `*.connector.ts` | one external API system |
|
|
83
|
+
| `*.resource.ts` | `resource()` and `mutation()` layer |
|
|
84
|
+
| `*.service.ts` | module singleton state |
|
|
85
|
+
| `*.guard.ts` | route guard |
|
|
86
|
+
| `*.routes.ts` | module-local route map |
|
|
87
|
+
| `*.public.ts` | only public module surface |
|
|
88
|
+
| `*.types.ts` | domain types |
|
|
89
|
+
| `*.component.ts` | Web Component registration |
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
Page-local signals, resources and forms live inside `view()`. Module-wide state
|
|
92
|
+
lives in `*.service.ts`.
|
|
88
93
|
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
## Data Flow
|
|
95
|
+
|
|
96
|
+
```txt
|
|
97
|
+
shared/http/http-client.ts
|
|
98
|
+
▲
|
|
99
|
+
modules/<x>/api/*.connector.ts DTO -> domain mapping
|
|
100
|
+
▲
|
|
101
|
+
modules/<x>/data/*.resource.ts cache keys + mutations
|
|
102
|
+
▲
|
|
103
|
+
modules/<x>/pages/*.page.ts UI consumes domain types
|
|
91
104
|
```
|
|
92
105
|
|
|
93
|
-
|
|
106
|
+
## Module Boundaries
|
|
107
|
+
|
|
108
|
+
Модуль opaque, кроме `<module>.public.ts`. Другие modules импортируют через этот
|
|
109
|
+
файл или никак. DTO внешних систем лежат в `_contracts/` и остаются private для
|
|
110
|
+
connector.
|
|
111
|
+
|
|
112
|
+
Default starter enforces this with ESLint:
|
|
113
|
+
|
|
114
|
+
- no barrels (`index.ts`);
|
|
115
|
+
- no `export *`;
|
|
116
|
+
- no CSS imports outside `src/main.ts`;
|
|
117
|
+
- no cross-module imports except public surfaces;
|
|
118
|
+
- no `_contracts` imports outside connectors.
|
|
119
|
+
|
|
120
|
+
## Styles
|
|
121
|
+
|
|
122
|
+
| File | Role |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| `src/shared/styles/tokens.css` | design tokens as CSS custom properties |
|
|
125
|
+
| `src/shared/styles/reset.css` | document/light DOM reset |
|
|
126
|
+
| `src/shared/styles/shell.css` | app-zone layouts from `src/layouts/` |
|
|
127
|
+
| `src/shared/styles/content.css` | page-level forms, tables, prose and states |
|
|
128
|
+
|
|
129
|
+
Reusable leaf components keep their own styles in ``css`...` `` inside
|
|
130
|
+
`component()` options and depend on tokens, not global classes.
|
|
131
|
+
|
|
132
|
+
`vite.config.ts` opts into Vite Lightning CSS transformer. Mado does not own
|
|
133
|
+
prefixing, CSS lowering or minification.
|
|
134
|
+
|
|
135
|
+
## CLI
|
|
136
|
+
|
|
137
|
+
Use `mado new` for boring file scaffolding:
|
|
94
138
|
|
|
95
139
|
```bash
|
|
96
|
-
mado
|
|
97
|
-
|
|
140
|
+
mado new module billing
|
|
141
|
+
mado new page billing/pages/invoices-list
|
|
142
|
+
mado new connector billing/api/stripe
|
|
143
|
+
mado new resource billing/data/invoices
|
|
144
|
+
mado new service billing/cart
|
|
145
|
+
mado new form billing/invoice
|
|
146
|
+
mado new component billing/components/invoice-status-badge
|
|
147
|
+
mado new guard billing/billing
|
|
148
|
+
mado new layout app-shell
|
|
98
149
|
```
|
|
99
150
|
|
|
100
|
-
|
|
151
|
+
The generator writes files only. It does not edit `app.routes.ts`, does not scan
|
|
152
|
+
the filesystem, and refuses to overwrite existing files.
|
package/docs/ru/11-layouts.md
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
# Layouts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Не
|
|
5
|
-
если
|
|
3
|
+
Blessed layout способ в Mado — route group в `src/app.routes.ts`.
|
|
4
|
+
Не заворачивайте каждую страницу вручную и не кладите общий shell в `main.ts`,
|
|
5
|
+
если есть разные зоны: public, auth, app, embed.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { layout, routes } from "@madojs/mado";
|
|
9
|
-
import { requireAuth } from "./
|
|
9
|
+
import { requireAuth } from "./modules/auth/auth.public";
|
|
10
|
+
import { authRoutes } from "./modules/auth/auth.routes";
|
|
11
|
+
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
10
12
|
|
|
11
13
|
export const manifest = {
|
|
12
|
-
"/": () => import("./
|
|
14
|
+
"/": () => import("./modules/home/home.page.js"),
|
|
13
15
|
"/login": layout({
|
|
14
|
-
layout: () => import("./layouts/auth.js"),
|
|
15
|
-
routes:
|
|
16
|
+
layout: () => import("./layouts/auth-shell.layout.js"),
|
|
17
|
+
routes: authRoutes,
|
|
16
18
|
}),
|
|
17
|
-
"/
|
|
18
|
-
layout: () => import("./layouts/app.js"),
|
|
19
|
+
"/billing": layout({
|
|
20
|
+
layout: () => import("./layouts/app-shell.layout.js"),
|
|
19
21
|
guard: requireAuth,
|
|
20
|
-
routes:
|
|
21
|
-
"/": () => import("./pages/admin/dashboard.js"),
|
|
22
|
-
"/orders": () => import("./pages/admin/orders.js"),
|
|
23
|
-
},
|
|
22
|
+
routes: billingRoutes,
|
|
24
23
|
}),
|
|
25
|
-
"*": () => import("./
|
|
24
|
+
"*": () => import("./modules/home/not-found.page.js"),
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
export default routes(manifest);
|
|
29
28
|
```
|
|
30
29
|
|
|
31
|
-
Layout —
|
|
30
|
+
Layout — обычный `page({ view })`, который рендерит `child`:
|
|
32
31
|
|
|
33
32
|
```ts
|
|
34
33
|
export default page({
|
|
35
|
-
view: ({ child }) => html
|
|
34
|
+
view: ({ child }) => html`
|
|
35
|
+
<div class="layout layout--app">
|
|
36
|
+
<main class="app-main">${child}</main>
|
|
37
|
+
</div>
|
|
38
|
+
`,
|
|
36
39
|
});
|
|
37
40
|
```
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
Rules:
|
|
40
43
|
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
- guard
|
|
44
|
-
- layout
|
|
45
|
-
|
|
46
|
-
Single-shell wrapper в `main.ts` допустим только для приложений, где абсолютно
|
|
47
|
-
все роуты живут в одной оболочке.
|
|
44
|
+
- one shell per route group, not per page;
|
|
45
|
+
- modules export plain route maps and do not call `layout()`;
|
|
46
|
+
- guard on a group protects the whole subtree;
|
|
47
|
+
- layout view stays stateless; page-local state lives in pages/components/resources.
|
|
@@ -1,53 +1,75 @@
|
|
|
1
1
|
# Auth and API
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Default starter — blessed recipe. HTTP mechanics живут в `src/shared/http/`,
|
|
4
|
+
auth state — в `src/modules/auth/`.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
```txt
|
|
7
|
+
src/shared/http/
|
|
8
|
+
http-client.ts
|
|
9
|
+
http-error.ts
|
|
10
|
+
interceptors.ts
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
src/modules/auth/
|
|
13
|
+
auth.connector.ts
|
|
14
|
+
auth.service.ts
|
|
15
|
+
auth.guard.ts
|
|
16
|
+
auth.routes.ts
|
|
17
|
+
auth.public.ts
|
|
18
|
+
_contracts/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Flow для business module:
|
|
22
|
+
|
|
23
|
+
```txt
|
|
24
|
+
connector -> resource/mutation -> page
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Pages не импортируют DTOs и не вызывают `fetch()` напрямую. Connectors не
|
|
28
|
+
импортируют Mado reactivity или UI.
|
|
10
29
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- protected routes закрываются group guard.
|
|
30
|
+
## Auth Service
|
|
31
|
+
|
|
32
|
+
Auth state — ES module singleton:
|
|
15
33
|
|
|
16
34
|
```ts
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
const _user = signal<User | null>(null);
|
|
36
|
+
const _token = signal<string | null>(null);
|
|
37
|
+
|
|
38
|
+
export const user = () => _user();
|
|
39
|
+
export const isAuthed = computed(() => _user() !== null);
|
|
22
40
|
```
|
|
23
41
|
|
|
24
|
-
|
|
42
|
+
Expose only what other modules need through `auth.public.ts`.
|
|
43
|
+
|
|
44
|
+
## Guards
|
|
25
45
|
|
|
26
46
|
```ts
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}),
|
|
47
|
+
export function requireAuth(): boolean | string {
|
|
48
|
+
if (isAuthed()) return true;
|
|
49
|
+
return "/login";
|
|
50
|
+
}
|
|
32
51
|
```
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
Use in `src/app.routes.ts`:
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
```ts
|
|
56
|
+
"/billing": layout({
|
|
57
|
+
layout: () => import("./layouts/app-shell.layout"),
|
|
58
|
+
guard: requireAuth,
|
|
59
|
+
routes: billingRoutes,
|
|
60
|
+
}),
|
|
61
|
+
```
|
|
41
62
|
|
|
42
|
-
|
|
63
|
+
## Dev Proxy
|
|
43
64
|
|
|
44
|
-
```
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
65
|
+
```ts
|
|
66
|
+
export default defineConfig({
|
|
67
|
+
plugins: [mado()],
|
|
68
|
+
server: {
|
|
69
|
+
proxy: { "/api": "http://localhost:3000" },
|
|
70
|
+
},
|
|
71
|
+
});
|
|
50
72
|
```
|
|
51
73
|
|
|
52
|
-
|
|
53
|
-
|
|
74
|
+
Rule: `shared/http` knows HTTP, connectors know one external system, resources
|
|
75
|
+
know cache keys, pages know UI, `*.public.ts` is the cross-module surface.
|