@everystate/examples 1.0.0 → 1.0.1
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/README.md +15 -0
- package/everyState-core/001-counter/README.md +44 -0
- package/everyState-core/001-counter/index.html +79 -0
- package/everyState-core/002-counter-improved/README.md +44 -0
- package/everyState-core/002-counter-improved/index.html +83 -0
- package/everyState-core/003-input-reactive/README.md +44 -0
- package/everyState-core/003-input-reactive/index.html +68 -0
- package/everyState-core/004-computed-state/README.md +45 -0
- package/everyState-core/004-computed-state/index.html +83 -0
- package/everyState-core/005-conditional-rendering/README.md +42 -0
- package/everyState-core/005-conditional-rendering/index.html +68 -0
- package/everyState-core/006-list-rendering/README.md +49 -0
- package/everyState-core/006-list-rendering/index.html +92 -0
- package/everyState-core/007-form-validation/README.md +52 -0
- package/everyState-core/007-form-validation/index.html +108 -0
- package/everyState-core/008-undo-redo/README.md +70 -0
- package/everyState-core/008-undo-redo/index.html +133 -0
- package/everyState-core/009-localStorage-side-effects/README.md +72 -0
- package/everyState-core/009-localStorage-side-effects/index.html +80 -0
- package/everyState-core/010-decoupled-components/README.md +74 -0
- package/everyState-core/010-decoupled-components/index.html +117 -0
- package/everyState-core/011-async-patterns/README.md +98 -0
- package/everyState-core/011-async-patterns/index.html +132 -0
- package/everyState-css/001-stateDrivenCSS/index.html +377 -0
- package/everyState-css/002-cssV2FullDemo/index.html +630 -0
- package/everyState-view/001/counter/index.css +31 -0
- package/everyState-view/001/counter/index.html +50 -0
- package/everyState-view/002/datatable/index.css +70 -0
- package/everyState-view/002/datatable/index.html +118 -0
- package/everyState-view/003/todo/index.css +260 -0
- package/everyState-view/003/todo/index.html +218 -0
- package/everyState-view/003-input-reactive/README.md +44 -0
- package/everyState-view/003-input-reactive/index.html +68 -0
- package/everyState-view/004/quotesFetcher/index.css +124 -0
- package/everyState-view/004/quotesFetcher/index.html +108 -0
- package/everyState-view/004_01/quotesFetcher/app.js +32 -0
- package/everyState-view/004_01/quotesFetcher/components/appHeader/appSubtitle.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/appHeader/appTitle.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/appHeader.js +9 -0
- package/everyState-view/004_01/quotesFetcher/components/historyHeading.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/historyList/histAuthor.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/historyList/histQuote.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/historyList.js +14 -0
- package/everyState-view/004_01/quotesFetcher/components/quoteCard/fetchButton.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/quoteCard/quoteAuthor.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/quoteCard/quoteMessage.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/quoteCard/quoteText.js +2 -0
- package/everyState-view/004_01/quotesFetcher/components/quoteCard.js +11 -0
- package/everyState-view/004_01/quotesFetcher/index.css +124 -0
- package/everyState-view/004_01/quotesFetcher/index.html +23 -0
- package/everyState-view/004_01/quotesFetcher/store.js +35 -0
- package/everyState-view/004_02/quotesFetcher/app.js +20 -0
- package/everyState-view/004_02/quotesFetcher/components.js +46 -0
- package/everyState-view/004_02/quotesFetcher/index.css +124 -0
- package/everyState-view/004_02/quotesFetcher/index.html +23 -0
- package/everyState-view/004_02/quotesFetcher/store.js +35 -0
- package/everyState-view/004_03/quotesFetcher/actions.js +27 -0
- package/everyState-view/004_03/quotesFetcher/app.js +19 -0
- package/everyState-view/004_03/quotesFetcher/components.js +28 -0
- package/everyState-view/004_03/quotesFetcher/index.css +124 -0
- package/everyState-view/004_03/quotesFetcher/index.html +23 -0
- package/everyState-view/004_03/quotesFetcher/resolve.js +34 -0
- package/everyState-view/004_03/quotesFetcher/store.js +11 -0
- package/everyState-view/004_04/quotesFetcher/actions.js +66 -0
- package/everyState-view/004_04/quotesFetcher/app.js +24 -0
- package/everyState-view/004_04/quotesFetcher/components/archive.js +40 -0
- package/everyState-view/004_04/quotesFetcher/components/fetcher.js +29 -0
- package/everyState-view/004_04/quotesFetcher/components.js +20 -0
- package/everyState-view/004_04/quotesFetcher/index.css +283 -0
- package/everyState-view/004_04/quotesFetcher/index.html +24 -0
- package/everyState-view/004_04/quotesFetcher/resolve.js +34 -0
- package/everyState-view/004_04/quotesFetcher/store.js +21 -0
- package/everyState-view/004_04/statedump.json +826 -0
- package/everyState-view/004_05/quoteExplorer/actions.js +58 -0
- package/everyState-view/004_05/quoteExplorer/app.js +27 -0
- package/everyState-view/004_05/quoteExplorer/components.js +83 -0
- package/everyState-view/004_05/quoteExplorer/index.css +231 -0
- package/everyState-view/004_05/quoteExplorer/index.html +23 -0
- package/everyState-view/004_05/quoteExplorer/resolve.js +50 -0
- package/everyState-view/004_05/quoteExplorer/store.js +33 -0
- package/everyState-view/004_06/quoteExplorer/actions.js +21 -0
- package/everyState-view/004_06/quoteExplorer/app.js +44 -0
- package/everyState-view/004_06/quoteExplorer/components.js +80 -0
- package/everyState-view/004_06/quoteExplorer/derived.js +43 -0
- package/everyState-view/004_06/quoteExplorer/index.css +346 -0
- package/everyState-view/004_06/quoteExplorer/index.html +25 -0
- package/everyState-view/004_06/quoteExplorer/intents.js +44 -0
- package/everyState-view/004_06/quoteExplorer/policies.js +25 -0
- package/everyState-view/004_06/quoteExplorer/resolve.js +51 -0
- package/everyState-view/004_06/quoteExplorer/store.js +44 -0
- package/everyState-view/004_07/quoteExplorer/app.js +47 -0
- package/everyState-view/004_07/quoteExplorer/components.js +85 -0
- package/everyState-view/004_07/quoteExplorer/derived.js +43 -0
- package/everyState-view/004_07/quoteExplorer/index.css +346 -0
- package/everyState-view/004_07/quoteExplorer/index.html +25 -0
- package/everyState-view/004_07/quoteExplorer/intents.js +51 -0
- package/everyState-view/004_07/quoteExplorer/policies.js +21 -0
- package/everyState-view/004_07/quoteExplorer/resolve.js +39 -0
- package/everyState-view/004_07/quoteExplorer/store.js +44 -0
- package/everyState-view/004_08/quoteExplorer/app.js +78 -0
- package/everyState-view/004_08/quoteExplorer/components.js +85 -0
- package/everyState-view/004_08/quoteExplorer/derived.js +43 -0
- package/everyState-view/004_08/quoteExplorer/index.css +346 -0
- package/everyState-view/004_08/quoteExplorer/index.html +25 -0
- package/everyState-view/004_08/quoteExplorer/intents.js +51 -0
- package/everyState-view/004_08/quoteExplorer/policies.js +21 -0
- package/everyState-view/004_08/quoteExplorer/resolve.js +39 -0
- package/everyState-view/004_08/quoteExplorer/store.js +44 -0
- package/everyState-view/004_08_V2/app.js +78 -0
- package/everyState-view/004_08_V2/components/appDetail.js +8 -0
- package/everyState-view/004_08_V2/components/appDetailBar.js +7 -0
- package/everyState-view/004_08_V2/components/appDetailBarClose.js +8 -0
- package/everyState-view/004_08_V2/components/appDetailBarCount.js +7 -0
- package/everyState-view/004_08_V2/components/appDetailBarHeading.js +7 -0
- package/everyState-view/004_08_V2/components/appDetailQuotes.js +15 -0
- package/everyState-view/004_08_V2/components/appHeader.js +7 -0
- package/everyState-view/004_08_V2/components/appHeaderSubtitle.js +7 -0
- package/everyState-view/004_08_V2/components/appHeaderTitle.js +7 -0
- package/everyState-view/004_08_V2/components/appLog.js +7 -0
- package/everyState-view/004_08_V2/components/appLogHeading.js +7 -0
- package/everyState-view/004_08_V2/components/appLogList.js +16 -0
- package/everyState-view/004_08_V2/components/appSearch.js +7 -0
- package/everyState-view/004_08_V2/components/appSearchInput.js +9 -0
- package/everyState-view/004_08_V2/components/appStats.js +7 -0
- package/everyState-view/004_08_V2/components/appStatsContent.js +8 -0
- package/everyState-view/004_08_V2/components/appStatsContentFavcount.js +7 -0
- package/everyState-view/004_08_V2/components/appStatsContentText.js +7 -0
- package/everyState-view/004_08_V2/components/appStatsToggle.js +8 -0
- package/everyState-view/004_08_V2/components/appTags.js +7 -0
- package/everyState-view/004_08_V2/components/appTagsLabel.js +7 -0
- package/everyState-view/004_08_V2/components/appTagsRow.js +15 -0
- package/everyState-view/004_08_V2/components/index.js +59 -0
- package/everyState-view/004_08_V2/components/utils/css.js +88 -0
- package/everyState-view/004_08_V2/components/utils/elements.js +87 -0
- package/everyState-view/004_08_V2/components.js +79 -0
- package/everyState-view/004_08_V2/derived.js +43 -0
- package/everyState-view/004_08_V2/index.css +350 -0
- package/everyState-view/004_08_V2/index.html +25 -0
- package/everyState-view/004_08_V2/intents.js +51 -0
- package/everyState-view/004_08_V2/policies.js +21 -0
- package/everyState-view/004_08_V2/resolve.js +39 -0
- package/everyState-view/004_08_V2/store.js +44 -0
- package/everyState-view/006/api-datatable/index.css +388 -0
- package/everyState-view/006/api-datatable/index.html +355 -0
- package/everyState-view/007/apiUsers/index.html +307 -0
- package/everyState-view/007-form-validation/README.md +52 -0
- package/everyState-view/007-form-validation/index.html +108 -0
- package/everyState-view/010-decoupled-components/README.md +74 -0
- package/everyState-view/010-decoupled-components/index.html +117 -0
- package/everyState-view/index.html +36 -0
- package/index.js +0 -5
- package/package.json +2 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createEveryState } from '@everystate/core';
|
|
2
|
+
import { createQueryClient } from '@everystate/core/queryClient';
|
|
3
|
+
|
|
4
|
+
export const store = createEveryState({
|
|
5
|
+
quote: '',
|
|
6
|
+
author: '',
|
|
7
|
+
history: [],
|
|
8
|
+
message: 'Click the button to fetch a quote'
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const qc = createQueryClient(store);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// ─── Action registry ───────────────────────────────────────────
|
|
2
|
+
// Every action is a dot-path key → function(store, qc).
|
|
3
|
+
// Components reference actions as 'actions.<key>'.
|
|
4
|
+
|
|
5
|
+
export const actions = {
|
|
6
|
+
|
|
7
|
+
'fetchQuote': async (store, qc) => {
|
|
8
|
+
store.set('message', 'Fetching...');
|
|
9
|
+
try {
|
|
10
|
+
const data = await qc.query('quote', async (signal) => {
|
|
11
|
+
const res = await fetch('https://dummyjson.com/quotes/random', { signal });
|
|
12
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
13
|
+
return res.json();
|
|
14
|
+
});
|
|
15
|
+
store.set('quote', data.quote);
|
|
16
|
+
store.set('author', data.author);
|
|
17
|
+
store.set('message', '');
|
|
18
|
+
const history = store.get('history') || [];
|
|
19
|
+
store.set('history', [{ quote: data.quote, author: data.author }, ...history].slice(0, 10));
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (err.name !== 'AbortError') {
|
|
22
|
+
store.set('message', 'Error: ' + err.message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
'loadArchive': async (store, qc) => {
|
|
28
|
+
store.set('archive.status', 'Loading 50 quotes...');
|
|
29
|
+
try {
|
|
30
|
+
const data = await qc.query('archive', async (signal) => {
|
|
31
|
+
const res = await fetch('https://dummyjson.com/quotes?limit=50', { signal });
|
|
32
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
33
|
+
return res.json();
|
|
34
|
+
});
|
|
35
|
+
const quotes = data.quotes.map(q => ({
|
|
36
|
+
quote: q.quote,
|
|
37
|
+
author: q.author,
|
|
38
|
+
id: '#' + q.id
|
|
39
|
+
}));
|
|
40
|
+
quotes.sort((a, b) => a.author.localeCompare(b.author));
|
|
41
|
+
const authors = [...new Set(quotes.map(q => q.author))];
|
|
42
|
+
store.set('archive.display', quotes);
|
|
43
|
+
store.set('archive.count', quotes.length);
|
|
44
|
+
store.set('archive.authorCount', authors.length);
|
|
45
|
+
store.set('archive.sortOrder', 'asc');
|
|
46
|
+
store.set('archive.status', '');
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.name !== 'AbortError') {
|
|
49
|
+
store.set('archive.status', 'Error: ' + err.message);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
'sortArchive': (store) => {
|
|
55
|
+
const current = store.get('archive.sortOrder') || 'asc';
|
|
56
|
+
const next = current === 'asc' ? 'desc' : 'asc';
|
|
57
|
+
const display = [...(store.get('archive.display') || [])];
|
|
58
|
+
display.sort((a, b) => {
|
|
59
|
+
const cmp = a.author.localeCompare(b.author);
|
|
60
|
+
return next === 'asc' ? cmp : -cmp;
|
|
61
|
+
});
|
|
62
|
+
store.set('archive.display', display);
|
|
63
|
+
store.set('archive.sortOrder', next);
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { flatten } from '@everystate/view/resolve';
|
|
2
|
+
import { mount } from '@everystate/view/project';
|
|
3
|
+
import { createPerfMonitor, mountOverlay } from '@everystate/perf';
|
|
4
|
+
import { store, qc } from './store.js';
|
|
5
|
+
import { c } from './components.js';
|
|
6
|
+
import { actions } from './actions.js';
|
|
7
|
+
import { resolveTree, resolveActions } from './resolve.js';
|
|
8
|
+
|
|
9
|
+
// 1. Resolve component tree from dot-path registry
|
|
10
|
+
const spec = resolveTree(c, 'app');
|
|
11
|
+
|
|
12
|
+
// 2. Resolve action handlers from dot-path registry
|
|
13
|
+
const handlers = resolveActions(actions, store, qc);
|
|
14
|
+
|
|
15
|
+
// 3. Flatten + Mount
|
|
16
|
+
flatten(spec, store, 'view');
|
|
17
|
+
mount(store, 'view', document.getElementById('app'), handlers);
|
|
18
|
+
|
|
19
|
+
// 4. Perf overlay with state tree
|
|
20
|
+
const perf = createPerfMonitor(store);
|
|
21
|
+
mountOverlay(perf, document.body);
|
|
22
|
+
|
|
23
|
+
// 5. Fire initial fetch
|
|
24
|
+
handlers['actions.fetchQuote']();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// ─── Archive sub-registry ──────────────────────────────────────
|
|
2
|
+
// 50-quote archive organized by author, with sort controls and stats.
|
|
3
|
+
// Deepest path: archive.content.card.meta.author (5 levels from archive)
|
|
4
|
+
// archive.toolbar.actions.sortBtn (4 levels from archive)
|
|
5
|
+
// archive.toolbar.stats.authors (4 levels from archive)
|
|
6
|
+
|
|
7
|
+
export const archiveComponents = {
|
|
8
|
+
|
|
9
|
+
// ── Section wrapper (level 1) ──
|
|
10
|
+
'archive': { tag: 'section', class: 'archive', children: ['archive.toolbar', 'archive.content'] },
|
|
11
|
+
|
|
12
|
+
// ── Toolbar (level 2) ──
|
|
13
|
+
'archive.toolbar': { tag: 'div', class: 'archive-toolbar', children: ['archive.toolbar.title', 'archive.toolbar.actions', 'archive.toolbar.stats'] },
|
|
14
|
+
'archive.toolbar.title': { tag: 'h2', class: 'section-heading', text: 'Quote Archive' },
|
|
15
|
+
|
|
16
|
+
// ── Toolbar → Actions (level 3) ──
|
|
17
|
+
'archive.toolbar.actions': { tag: 'div', class: 'archive-actions', children: ['archive.toolbar.actions.loadBtn', 'archive.toolbar.actions.sortBtn'] },
|
|
18
|
+
'archive.toolbar.actions.loadBtn': { tag: 'button', class: 'btn btn-primary', text: 'Load 50 Quotes', onClick: 'actions.loadArchive' },
|
|
19
|
+
'archive.toolbar.actions.sortBtn': { tag: 'button', class: 'btn btn-secondary', text: '↕ Sort by Author', onClick: 'actions.sortArchive' },
|
|
20
|
+
|
|
21
|
+
// ── Toolbar → Stats (level 3) ──
|
|
22
|
+
'archive.toolbar.stats': { tag: 'div', class: 'archive-stats', children: ['archive.toolbar.stats.count', 'archive.toolbar.stats.authors'] },
|
|
23
|
+
'archive.toolbar.stats.count': { tag: 'span', class: 'stat-badge', text: '{archive.count} quotes' },
|
|
24
|
+
'archive.toolbar.stats.authors': { tag: 'span', class: 'stat-badge', text: '{archive.authorCount} authors' },
|
|
25
|
+
|
|
26
|
+
// ── Content (level 2) ──
|
|
27
|
+
'archive.content': { tag: 'div', class: 'archive-content', children: ['archive.content.status', 'archive.content.list'] },
|
|
28
|
+
'archive.content.status': { tag: 'p', class: 'archive-status', text: '{archive.status}' },
|
|
29
|
+
'archive.content.list': { tag: 'div', class: 'archive-list', forEach: 'archive.display', as: 'item', template: 'archive.content.card' },
|
|
30
|
+
|
|
31
|
+
// ── Quote card template (level 3 from content) ──
|
|
32
|
+
'archive.content.card': { tag: 'div', class: 'archive-card', children: ['archive.content.card.quote', 'archive.content.card.meta'] },
|
|
33
|
+
'archive.content.card.quote': { tag: 'p', class: 'archive-quote-text', text: 'item.quote' },
|
|
34
|
+
|
|
35
|
+
// ── Card → Meta (level 4 from content) ──
|
|
36
|
+
'archive.content.card.meta': { tag: 'div', class: 'archive-card-meta', children: ['archive.content.card.meta.author', 'archive.content.card.meta.id'] },
|
|
37
|
+
'archive.content.card.meta.author': { tag: 'span', class: 'archive-card-author', text: 'item.author' },
|
|
38
|
+
'archive.content.card.meta.id': { tag: 'span', class: 'archive-card-id', text: 'item.id' },
|
|
39
|
+
|
|
40
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// ─── Fetcher sub-registry ──────────────────────────────────────
|
|
2
|
+
// Random quote fetcher with history list.
|
|
3
|
+
// Nesting: fetcher → fetcher.card → fetcher.card.text (3 levels)
|
|
4
|
+
// fetcher → fetcher.history → fetcher.history.list → fetcher.history.item → fetcher.history.item.quote (5 levels)
|
|
5
|
+
|
|
6
|
+
export const fetcherComponents = {
|
|
7
|
+
|
|
8
|
+
// ── Section wrapper ──
|
|
9
|
+
'fetcher': { tag: 'section', class: 'fetcher', children: ['fetcher.heading', 'fetcher.card', 'fetcher.history'] },
|
|
10
|
+
'fetcher.heading': { tag: 'h2', class: 'section-heading', text: 'Random Quote' },
|
|
11
|
+
|
|
12
|
+
// ── Card (level 2) ──
|
|
13
|
+
'fetcher.card': { tag: 'div', class: 'card', children: ['fetcher.card.text', 'fetcher.card.author', 'fetcher.card.message', 'fetcher.card.btn'] },
|
|
14
|
+
'fetcher.card.text': { tag: 'p', class: 'quote-text', text: '{quote}' },
|
|
15
|
+
'fetcher.card.author': { tag: 'p', class: 'quote-author', text: '{author}' },
|
|
16
|
+
'fetcher.card.message': { tag: 'p', class: 'message', text: '{message}' },
|
|
17
|
+
'fetcher.card.btn': { tag: 'button', class: 'btn btn-primary', text: 'New Quote', onClick: 'actions.fetchQuote' },
|
|
18
|
+
|
|
19
|
+
// ── History (level 2) ──
|
|
20
|
+
'fetcher.history': { tag: 'div', class: 'fetcher-history', children: ['fetcher.history.heading', 'fetcher.history.list'] },
|
|
21
|
+
'fetcher.history.heading': { tag: 'h3', class: 'history-heading', text: 'Recent ({history.length})' },
|
|
22
|
+
'fetcher.history.list': { tag: 'ul', class: 'history', forEach: 'history', as: 'item', template: 'fetcher.history.item' },
|
|
23
|
+
|
|
24
|
+
// ── History item template (level 3–4) ──
|
|
25
|
+
'fetcher.history.item': { tag: 'li', class: 'history-item', children: ['fetcher.history.item.quote', 'fetcher.history.item.author'] },
|
|
26
|
+
'fetcher.history.item.quote': { tag: 'span', class: 'hist-quote', text: 'item.quote' },
|
|
27
|
+
'fetcher.history.item.author': { tag: 'span', class: 'hist-author', text: 'item.author' },
|
|
28
|
+
|
|
29
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ─── Component registry ────────────────────────────────────────
|
|
2
|
+
// Root shell + merged sub-registries.
|
|
3
|
+
// Complex subtrees extracted to components/fetcher.js and components/archive.js.
|
|
4
|
+
|
|
5
|
+
import { fetcherComponents } from './components/fetcher.js';
|
|
6
|
+
import { archiveComponents } from './components/archive.js';
|
|
7
|
+
|
|
8
|
+
export const c = {
|
|
9
|
+
|
|
10
|
+
// ── App Shell ──
|
|
11
|
+
'app': { tag: 'div', class: 'quotes-app', children: ['app.header', 'fetcher', 'archive'] },
|
|
12
|
+
'app.header': { tag: 'header', class: 'app-header', children: ['app.header.title', 'app.header.subtitle'] },
|
|
13
|
+
'app.header.title': { tag: 'h1', text: '💬 Quote Explorer' },
|
|
14
|
+
'app.header.subtitle': { tag: 'p', class: 'subtitle', text: 'Uniform Resource Architecture - everything is a dot path' },
|
|
15
|
+
|
|
16
|
+
// ── Merged sub-registries ──
|
|
17
|
+
...fetcherComponents,
|
|
18
|
+
...archiveComponents,
|
|
19
|
+
|
|
20
|
+
};
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/* ─── Base ───────────────────────────────────────────────────── */
|
|
2
|
+
:root {
|
|
3
|
+
--color-bg: #f0f2f5;
|
|
4
|
+
--color-surface: #ffffff;
|
|
5
|
+
--color-primary: #6750a4;
|
|
6
|
+
--color-primary-light: #e8def8;
|
|
7
|
+
--color-text: #1c1b1f;
|
|
8
|
+
--color-text-secondary: #49454f;
|
|
9
|
+
--color-text-muted: #79747e;
|
|
10
|
+
--color-border: #e0e0e0;
|
|
11
|
+
--color-divider: #cac4d0;
|
|
12
|
+
--radius: 12px;
|
|
13
|
+
--radius-sm: 8px;
|
|
14
|
+
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
15
|
+
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
22
|
+
background: var(--color-bg);
|
|
23
|
+
color: var(--color-text);
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* ─── App Shell ──────────────────────────────────────────────── */
|
|
28
|
+
.quotes-app {
|
|
29
|
+
max-width: 960px;
|
|
30
|
+
margin: 0 auto;
|
|
31
|
+
padding: 2rem 1.5rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.app-header {
|
|
35
|
+
text-align: center;
|
|
36
|
+
margin-bottom: 2.5rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.app-header h1 {
|
|
40
|
+
font-size: 2rem;
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
color: var(--color-primary);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.subtitle {
|
|
46
|
+
color: var(--color-text-muted);
|
|
47
|
+
font-size: 0.9rem;
|
|
48
|
+
margin-top: 0.25rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ─── Section Headings ───────────────────────────────────────── */
|
|
52
|
+
.section-heading {
|
|
53
|
+
font-size: 1.35rem;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
margin-bottom: 1rem;
|
|
56
|
+
color: var(--color-text);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ─── Buttons ────────────────────────────────────────────────── */
|
|
60
|
+
.btn {
|
|
61
|
+
display: inline-flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
gap: 0.4rem;
|
|
64
|
+
padding: 0.55rem 1.25rem;
|
|
65
|
+
border: none;
|
|
66
|
+
border-radius: 999px;
|
|
67
|
+
font-size: 0.9rem;
|
|
68
|
+
font-weight: 500;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
transition: all 0.15s ease;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.btn-primary {
|
|
74
|
+
background: var(--color-primary);
|
|
75
|
+
color: #fff;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.btn-primary:hover {
|
|
79
|
+
background: #7b68b6;
|
|
80
|
+
box-shadow: var(--shadow);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.btn-secondary {
|
|
84
|
+
background: var(--color-primary-light);
|
|
85
|
+
color: var(--color-primary);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.btn-secondary:hover {
|
|
89
|
+
background: #d9cdf0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* ─── Fetcher Section ────────────────────────────────────────── */
|
|
93
|
+
.fetcher {
|
|
94
|
+
margin-bottom: 3rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.card {
|
|
98
|
+
background: var(--color-surface);
|
|
99
|
+
border-radius: var(--radius);
|
|
100
|
+
padding: 1.75rem;
|
|
101
|
+
box-shadow: var(--shadow);
|
|
102
|
+
margin-bottom: 1.5rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.quote-text {
|
|
106
|
+
font-size: 1.2rem;
|
|
107
|
+
font-style: italic;
|
|
108
|
+
line-height: 1.7;
|
|
109
|
+
color: var(--color-text);
|
|
110
|
+
margin-bottom: 0.75rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.quote-text::before { content: '\201C'; }
|
|
114
|
+
.quote-text::after { content: '\201D'; }
|
|
115
|
+
|
|
116
|
+
.quote-author {
|
|
117
|
+
color: var(--color-text-secondary);
|
|
118
|
+
font-weight: 500;
|
|
119
|
+
margin-bottom: 0.5rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.quote-author::before { content: '- '; }
|
|
123
|
+
|
|
124
|
+
.message {
|
|
125
|
+
color: var(--color-text-muted);
|
|
126
|
+
font-size: 0.85rem;
|
|
127
|
+
min-height: 1.2em;
|
|
128
|
+
margin-bottom: 1rem;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ─── Fetcher History ────────────────────────────────────────── */
|
|
132
|
+
.fetcher-history {
|
|
133
|
+
margin-top: 0.5rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.history-heading {
|
|
137
|
+
font-size: 1rem;
|
|
138
|
+
font-weight: 600;
|
|
139
|
+
color: var(--color-text-secondary);
|
|
140
|
+
margin-bottom: 0.75rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.history {
|
|
144
|
+
list-style: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.history-item {
|
|
148
|
+
display: flex;
|
|
149
|
+
gap: 0.75rem;
|
|
150
|
+
padding: 0.6rem 0;
|
|
151
|
+
border-bottom: 1px solid var(--color-border);
|
|
152
|
+
font-size: 0.9rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.history-item:last-child {
|
|
156
|
+
border-bottom: none;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.hist-quote {
|
|
160
|
+
flex: 1;
|
|
161
|
+
color: var(--color-text);
|
|
162
|
+
white-space: nowrap;
|
|
163
|
+
overflow: hidden;
|
|
164
|
+
text-overflow: ellipsis;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.hist-author {
|
|
168
|
+
color: var(--color-text-muted);
|
|
169
|
+
font-style: italic;
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ─── Archive Section ────────────────────────────────────────── */
|
|
174
|
+
.archive {
|
|
175
|
+
border-top: 2px solid var(--color-divider);
|
|
176
|
+
padding-top: 2rem;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.archive-toolbar {
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
flex-wrap: wrap;
|
|
183
|
+
gap: 1rem;
|
|
184
|
+
margin-bottom: 1.5rem;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.archive-toolbar .section-heading {
|
|
188
|
+
margin-bottom: 0;
|
|
189
|
+
margin-right: auto;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.archive-actions {
|
|
193
|
+
display: flex;
|
|
194
|
+
gap: 0.5rem;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.archive-stats {
|
|
198
|
+
display: flex;
|
|
199
|
+
gap: 0.5rem;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.stat-badge {
|
|
203
|
+
display: inline-flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
background: var(--color-primary-light);
|
|
206
|
+
color: var(--color-primary);
|
|
207
|
+
padding: 0.25rem 0.75rem;
|
|
208
|
+
border-radius: 999px;
|
|
209
|
+
font-size: 0.8rem;
|
|
210
|
+
font-weight: 500;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* ─── Archive Content ────────────────────────────────────────── */
|
|
214
|
+
.archive-content {
|
|
215
|
+
min-height: 100px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.archive-status {
|
|
219
|
+
color: var(--color-text-muted);
|
|
220
|
+
font-style: italic;
|
|
221
|
+
text-align: center;
|
|
222
|
+
padding: 2rem 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.archive-list {
|
|
226
|
+
display: grid;
|
|
227
|
+
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
|
228
|
+
gap: 1rem;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* ─── Archive Card (deep composition) ────────────────────────── */
|
|
232
|
+
.archive-card {
|
|
233
|
+
background: var(--color-surface);
|
|
234
|
+
border-radius: var(--radius-sm);
|
|
235
|
+
padding: 1rem 1.25rem;
|
|
236
|
+
box-shadow: var(--shadow);
|
|
237
|
+
transition: box-shadow 0.15s ease, transform 0.15s ease;
|
|
238
|
+
display: flex;
|
|
239
|
+
flex-direction: column;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.archive-card:hover {
|
|
243
|
+
box-shadow: var(--shadow-md);
|
|
244
|
+
transform: translateY(-1px);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.archive-quote-text {
|
|
248
|
+
font-size: 0.9rem;
|
|
249
|
+
line-height: 1.6;
|
|
250
|
+
color: var(--color-text);
|
|
251
|
+
margin-bottom: 0.75rem;
|
|
252
|
+
flex: 1;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.archive-card-meta {
|
|
256
|
+
display: flex;
|
|
257
|
+
justify-content: space-between;
|
|
258
|
+
align-items: center;
|
|
259
|
+
padding-top: 0.5rem;
|
|
260
|
+
border-top: 1px solid var(--color-border);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.archive-card-author {
|
|
264
|
+
font-style: italic;
|
|
265
|
+
color: var(--color-text-secondary);
|
|
266
|
+
font-size: 0.82rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.archive-card-author::before { content: '- '; }
|
|
270
|
+
|
|
271
|
+
.archive-card-id {
|
|
272
|
+
color: var(--color-text-muted);
|
|
273
|
+
font-size: 0.75rem;
|
|
274
|
+
font-family: monospace;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* ─── Responsive ─────────────────────────────────────────────── */
|
|
278
|
+
@media (max-width: 600px) {
|
|
279
|
+
.quotes-app { padding: 1rem; }
|
|
280
|
+
.app-header h1 { font-size: 1.5rem; }
|
|
281
|
+
.archive-toolbar { flex-direction: column; align-items: flex-start; }
|
|
282
|
+
.archive-list { grid-template-columns: 1fr; }
|
|
283
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>@everystate/view: Quote Explorer (Uniform Resource Architecture)</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{
|
|
9
|
+
"imports": {
|
|
10
|
+
"@everystate/core": "../../../../everystate-core/index.js",
|
|
11
|
+
"@everystate/core/queryClient": "../../../../everystate-core/queryClient.js",
|
|
12
|
+
"@everystate/view/resolve": "../../../../everystate-view/resolve.js",
|
|
13
|
+
"@everystate/view/project": "../../../../everystate-view/project.js",
|
|
14
|
+
"@everystate/perf": "../../../../everystate-perf/index.js"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
<link rel="stylesheet" href="index.css">
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<div id="app"></div>
|
|
22
|
+
<script type="module" src="app.js"></script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// ─── Unified resolver ──────────────────────────────────────────
|
|
2
|
+
// Two functions, one pattern: dot paths → values.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* resolveTree - recursively builds a spec tree from the component registry.
|
|
6
|
+
* Replaces dot-path strings in children/template with resolved spec objects.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveTree(registry, key) {
|
|
9
|
+
const node = registry[key];
|
|
10
|
+
if (!node) return null;
|
|
11
|
+
const resolved = { ...node };
|
|
12
|
+
if (Array.isArray(resolved.children)) {
|
|
13
|
+
resolved.children = resolved.children.map(child =>
|
|
14
|
+
typeof child === 'string' ? resolveTree(registry, child) : child
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
if (typeof resolved.template === 'string') {
|
|
18
|
+
resolved.template = resolveTree(registry, resolved.template);
|
|
19
|
+
}
|
|
20
|
+
return resolved;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* resolveActions - builds a handlers map from the action registry.
|
|
25
|
+
* Maps 'actions.fetchQuote' → () => actions['fetchQuote'](store, ...deps)
|
|
26
|
+
* so mount() can wire onClick strings directly.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveActions(actionRegistry, store, ...deps) {
|
|
29
|
+
const handlers = {};
|
|
30
|
+
for (const [key, fn] of Object.entries(actionRegistry)) {
|
|
31
|
+
handlers[`actions.${key}`] = (...args) => fn(store, ...deps, ...args);
|
|
32
|
+
}
|
|
33
|
+
return handlers;
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createEveryState } from '@everystate/core';
|
|
2
|
+
import { createQueryClient } from '@everystate/core/queryClient';
|
|
3
|
+
|
|
4
|
+
export const store = createEveryState({
|
|
5
|
+
// ── Fetcher ──
|
|
6
|
+
quote: '',
|
|
7
|
+
author: '',
|
|
8
|
+
history: [],
|
|
9
|
+
message: 'Click the button to fetch a quote',
|
|
10
|
+
|
|
11
|
+
// ── Archive ──
|
|
12
|
+
archive: {
|
|
13
|
+
display: [],
|
|
14
|
+
count: 0,
|
|
15
|
+
authorCount: 0,
|
|
16
|
+
sortOrder: 'none',
|
|
17
|
+
status: 'Click "Load Archive" to fetch 50 quotes'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const qc = createQueryClient(store);
|