@invisibleloop/pulse 0.1.21
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/.claude/commands/build-page.md +59 -0
- package/.claude/commands/new-doc-page.md +45 -0
- package/.claude/commands/verify.md +52 -0
- package/.claude/pulse-checklist.md +111 -0
- package/.claude/settings.local.json +102 -0
- package/.github/workflows/ci.yml +22 -0
- package/.github/workflows/publish.yml +41 -0
- package/.pulse/load-reports/home/1773432711417.json +22 -0
- package/CLAUDE.md +383 -0
- package/README.md +95 -0
- package/docs/.claude/pulse-checklist.md +111 -0
- package/docs/public/.pulse-ui-version +1 -0
- package/docs/public/dist/accessibility.boot-5DVTARJU.js +115 -0
- package/docs/public/dist/actions.boot-P66HKQEM.js +164 -0
- package/docs/public/dist/auth.boot-IMAJAUPH.js +140 -0
- package/docs/public/dist/caching.boot-DVR6KDE7.js +53 -0
- package/docs/public/dist/components--accordion.boot-3HVKMNWC.js +11 -0
- package/docs/public/dist/components--alert.boot-GCEXOZAC.js +6 -0
- package/docs/public/dist/components--app-badge.boot-DVT3GCHJ.js +6 -0
- package/docs/public/dist/components--avatar.boot-PSW24EVA.js +5 -0
- package/docs/public/dist/components--badge.boot-TYDY2RMK.js +7 -0
- package/docs/public/dist/components--banner.boot-EI5PZSZK.js +7 -0
- package/docs/public/dist/components--breadcrumbs.boot-SMA2E2GO.js +34 -0
- package/docs/public/dist/components--button.boot-J54BQM2E.js +23 -0
- package/docs/public/dist/components--card.boot-PZGNDIB6.js +138 -0
- package/docs/public/dist/components--carousel.boot-TP6LPFZZ.js +12 -0
- package/docs/public/dist/components--charts.boot-2EOYQWKL.js +108 -0
- package/docs/public/dist/components--checkbox.boot-DS5BSL6T.js +54 -0
- package/docs/public/dist/components--cluster.boot-HHVIBBJG.js +9 -0
- package/docs/public/dist/components--code-window.boot-2GR2DV33.js +20 -0
- package/docs/public/dist/components--container.boot-7LOOGK2K.js +5 -0
- package/docs/public/dist/components--cta.boot-FSNZ5YRT.js +11 -0
- package/docs/public/dist/components--divider.boot-3NI2C3QG.js +6 -0
- package/docs/public/dist/components--empty.boot-YX2UR3PV.js +7 -0
- package/docs/public/dist/components--feature.boot-MUD7NSUO.js +13 -0
- package/docs/public/dist/components--fieldset.boot-J7BYHMKF.js +19 -0
- package/docs/public/dist/components--fileupload.boot-NIKVTTPD.js +52 -0
- package/docs/public/dist/components--footer.boot-EYUK5FRG.js +14 -0
- package/docs/public/dist/components--grid.boot-URDQVDDR.js +59 -0
- package/docs/public/dist/components--heading.boot-BPQKU43E.js +44 -0
- package/docs/public/dist/components--hero.boot-4RAPRGAB.js +17 -0
- package/docs/public/dist/components--icons.boot-ZITNU5JP.js +68 -0
- package/docs/public/dist/components--image.boot-XEEGHQZF.js +19 -0
- package/docs/public/dist/components--input.boot-SGASZG5K.js +7 -0
- package/docs/public/dist/components--list.boot-W3XC5MHD.js +55 -0
- package/docs/public/dist/components--media.boot-5VFIETZO.js +13 -0
- package/docs/public/dist/components--modal.boot-RZUYXBN2.js +47 -0
- package/docs/public/dist/components--nav.boot-ODBOHU7O.js +33 -0
- package/docs/public/dist/components--pricing.boot-4AQ4ZVBY.js +21 -0
- package/docs/public/dist/components--progress.boot-GHAGYZOK.js +30 -0
- package/docs/public/dist/components--prose.boot-QANJL6JI.js +67 -0
- package/docs/public/dist/components--pullquote.boot-Q2WMNAZU.js +22 -0
- package/docs/public/dist/components--radio.boot-TJRDQ2OL.js +75 -0
- package/docs/public/dist/components--rating.boot-QBAN6DEL.js +38 -0
- package/docs/public/dist/components--search.boot-PXH5O5AG.js +17 -0
- package/docs/public/dist/components--section.boot-AQGIYHWW.js +12 -0
- package/docs/public/dist/components--segmented.boot-BEVTKEJO.js +33 -0
- package/docs/public/dist/components--select.boot-47X5RHOC.js +10 -0
- package/docs/public/dist/components--slider.boot-PSRRX7XL.js +47 -0
- package/docs/public/dist/components--spinner.boot-MZ5MO2OH.js +22 -0
- package/docs/public/dist/components--stack.boot-DI4NJXBF.js +9 -0
- package/docs/public/dist/components--stat.boot-QMFUWBQT.js +9 -0
- package/docs/public/dist/components--stepper.boot-34PP2NEV.js +22 -0
- package/docs/public/dist/components--table.boot-FCQGSFIQ.js +11 -0
- package/docs/public/dist/components--testimonial.boot-DWQPDKYG.js +11 -0
- package/docs/public/dist/components--textarea.boot-QVXLBOJ5.js +4 -0
- package/docs/public/dist/components--timeline.boot-26LN52P2.js +95 -0
- package/docs/public/dist/components--toggle.boot-IQQEI76S.js +29 -0
- package/docs/public/dist/components--tooltip.boot-LGHCO6NN.js +9 -0
- package/docs/public/dist/components.boot-SE6PQ4P7.js +103 -0
- package/docs/public/dist/config.boot-DTRRWUE6.js +126 -0
- package/docs/public/dist/constraints.boot-DUHDZBMC.js +71 -0
- package/docs/public/dist/deploy.boot-SLAD3NI2.js +163 -0
- package/docs/public/dist/docs-8e3d4b5c.css +1 -0
- package/docs/public/dist/extending.boot-UA3CN243.js +159 -0
- package/docs/public/dist/faq.boot-6EQAWLQR.js +43 -0
- package/docs/public/dist/getting-started.boot-TDKIFL5U.js +86 -0
- package/docs/public/dist/guard.boot-AUHAWTG4.js +80 -0
- package/docs/public/dist/home.boot-BVQXRH32.js +383 -0
- package/docs/public/dist/how-it-works.boot-LTWAKWKW.js +104 -0
- package/docs/public/dist/hydration.boot-JRM6IPJL.js +78 -0
- package/docs/public/dist/images.boot-M6ZVKTZS.js +80 -0
- package/docs/public/dist/manifest.json +94 -0
- package/docs/public/dist/meta.boot-7NXGPHR4.js +79 -0
- package/docs/public/dist/mutations.boot-F6F43UDX.js +79 -0
- package/docs/public/dist/navigation.boot-AOXWS3ZF.js +57 -0
- package/docs/public/dist/performance.boot-C3UPCOBK.js +98 -0
- package/docs/public/dist/persist.boot-WT32PQOQ.js +61 -0
- package/docs/public/dist/project-structure.boot-FB3LRVJ4.js +63 -0
- package/docs/public/dist/prompt-examples.boot-YKR4VDK4.js +31 -0
- package/docs/public/dist/pulse-ui-81a85c03.css +1 -0
- package/docs/public/dist/raw-responses.boot-M4KA5YXL.js +104 -0
- package/docs/public/dist/routing.boot-FNX5FDGH.js +70 -0
- package/docs/public/dist/runtime-B73WLANC.js +1 -0
- package/docs/public/dist/runtime-KO4BHUQ3.js +49 -0
- package/docs/public/dist/runtime-L2HNXIHW.js +59 -0
- package/docs/public/dist/runtime-QFURDKA2.js +5 -0
- package/docs/public/dist/runtime-UVPXO4IR.js +375 -0
- package/docs/public/dist/runtime-VMJA3Z4N.js +10 -0
- package/docs/public/dist/runtime-ZJ4FXT5O.js +11 -0
- package/docs/public/dist/server-api.boot-K7X3LCFB.js +219 -0
- package/docs/public/dist/server-data.boot-Y7HQYC4R.js +157 -0
- package/docs/public/dist/slash-commands.boot-V2UV7OW2.js +26 -0
- package/docs/public/dist/spec.boot-2WU7ZHCV.js +159 -0
- package/docs/public/dist/state.boot-B24GUE3R.js +73 -0
- package/docs/public/dist/store.boot-TLIB4XHH.js +150 -0
- package/docs/public/dist/streaming.boot-W2DZSMW4.js +80 -0
- package/docs/public/dist/stripe.boot-QN3C2GEL.js +164 -0
- package/docs/public/dist/supabase.boot-BG4XXLZE.js +303 -0
- package/docs/public/dist/testing.boot-6U4WKMTE.js +130 -0
- package/docs/public/dist/validation.boot-PQHYGW5B.js +100 -0
- package/docs/public/docs.css +2020 -0
- package/docs/public/menu.js +83 -0
- package/docs/public/pulse-ui.css +2739 -0
- package/docs/public/pulse-ui.js +236 -0
- package/docs/server.js +192 -0
- package/docs/src/lib/component-page.js +47 -0
- package/docs/src/lib/highlight.js +255 -0
- package/docs/src/lib/layout.js +131 -0
- package/docs/src/lib/metrics-store.js +6 -0
- package/docs/src/lib/nav.js +159 -0
- package/docs/src/lib/stats.js +81 -0
- package/docs/src/pages/accessibility.js +157 -0
- package/docs/src/pages/actions.js +191 -0
- package/docs/src/pages/auth.js +177 -0
- package/docs/src/pages/caching.js +95 -0
- package/docs/src/pages/components/accordion.js +48 -0
- package/docs/src/pages/components/alert.js +35 -0
- package/docs/src/pages/components/app-badge.js +41 -0
- package/docs/src/pages/components/avatar.js +35 -0
- package/docs/src/pages/components/badge.js +36 -0
- package/docs/src/pages/components/banner.js +45 -0
- package/docs/src/pages/components/breadcrumbs.js +94 -0
- package/docs/src/pages/components/button.js +84 -0
- package/docs/src/pages/components/card.js +225 -0
- package/docs/src/pages/components/carousel.js +72 -0
- package/docs/src/pages/components/charts.js +278 -0
- package/docs/src/pages/components/checkbox.js +129 -0
- package/docs/src/pages/components/cluster.js +47 -0
- package/docs/src/pages/components/code-window.js +57 -0
- package/docs/src/pages/components/container.js +40 -0
- package/docs/src/pages/components/cta.js +53 -0
- package/docs/src/pages/components/divider.js +37 -0
- package/docs/src/pages/components/empty.js +36 -0
- package/docs/src/pages/components/feature.js +60 -0
- package/docs/src/pages/components/fieldset.js +65 -0
- package/docs/src/pages/components/fileupload.js +127 -0
- package/docs/src/pages/components/footer.js +58 -0
- package/docs/src/pages/components/grid.js +165 -0
- package/docs/src/pages/components/heading.js +107 -0
- package/docs/src/pages/components/hero.js +65 -0
- package/docs/src/pages/components/icons.js +285 -0
- package/docs/src/pages/components/image.js +71 -0
- package/docs/src/pages/components/input.js +51 -0
- package/docs/src/pages/components/list.js +112 -0
- package/docs/src/pages/components/media.js +51 -0
- package/docs/src/pages/components/modal.js +111 -0
- package/docs/src/pages/components/nav.js +86 -0
- package/docs/src/pages/components/pricing.js +68 -0
- package/docs/src/pages/components/progress.js +102 -0
- package/docs/src/pages/components/prose.js +111 -0
- package/docs/src/pages/components/pullquote.js +71 -0
- package/docs/src/pages/components/radio.js +194 -0
- package/docs/src/pages/components/rating.js +106 -0
- package/docs/src/pages/components/search.js +61 -0
- package/docs/src/pages/components/section.js +59 -0
- package/docs/src/pages/components/segmented.js +121 -0
- package/docs/src/pages/components/select.js +45 -0
- package/docs/src/pages/components/slider.js +114 -0
- package/docs/src/pages/components/spinner.js +73 -0
- package/docs/src/pages/components/stack.js +48 -0
- package/docs/src/pages/components/stat.js +55 -0
- package/docs/src/pages/components/stepper.js +66 -0
- package/docs/src/pages/components/table.js +45 -0
- package/docs/src/pages/components/testimonial.js +49 -0
- package/docs/src/pages/components/textarea.js +31 -0
- package/docs/src/pages/components/timeline.js +227 -0
- package/docs/src/pages/components/toggle.js +84 -0
- package/docs/src/pages/components/tooltip.js +48 -0
- package/docs/src/pages/components.js +204 -0
- package/docs/src/pages/config.js +193 -0
- package/docs/src/pages/constraints.js +99 -0
- package/docs/src/pages/deploy.js +233 -0
- package/docs/src/pages/extending.js +198 -0
- package/docs/src/pages/faq.js +96 -0
- package/docs/src/pages/getting-started.js +106 -0
- package/docs/src/pages/guard.js +121 -0
- package/docs/src/pages/home.js +401 -0
- package/docs/src/pages/how-it-works.js +183 -0
- package/docs/src/pages/hydration.js +98 -0
- package/docs/src/pages/images.js +121 -0
- package/docs/src/pages/meta.js +120 -0
- package/docs/src/pages/mutations.js +106 -0
- package/docs/src/pages/navigation.js +85 -0
- package/docs/src/pages/performance.js +157 -0
- package/docs/src/pages/persist.js +88 -0
- package/docs/src/pages/project-structure.js +90 -0
- package/docs/src/pages/prompt-examples.js +186 -0
- package/docs/src/pages/raw-responses.js +124 -0
- package/docs/src/pages/routing.js +99 -0
- package/docs/src/pages/server-api.js +281 -0
- package/docs/src/pages/server-data.js +185 -0
- package/docs/src/pages/slash-commands.js +55 -0
- package/docs/src/pages/spec.js +207 -0
- package/docs/src/pages/state.js +101 -0
- package/docs/src/pages/store.js +181 -0
- package/docs/src/pages/streaming.js +108 -0
- package/docs/src/pages/stripe.js +193 -0
- package/docs/src/pages/supabase.js +323 -0
- package/docs/src/pages/testing.js +198 -0
- package/docs/src/pages/validation.js +138 -0
- package/examples/contact.js +166 -0
- package/examples/counter.js +94 -0
- package/examples/dev.server.js +91 -0
- package/examples/examples.test.js +394 -0
- package/examples/pricing.js +244 -0
- package/examples/products.js +191 -0
- package/examples/quiz.js +208 -0
- package/examples/shared.js +78 -0
- package/examples/todos.js +162 -0
- package/package.json +75 -0
- package/public/.pulse-ui-version +1 -0
- package/public/chippy-bird.css +246 -0
- package/public/examples/contact.css +119 -0
- package/public/examples/counter.css +79 -0
- package/public/examples/pricing.css +132 -0
- package/public/examples/products.css +100 -0
- package/public/examples/quiz.css +200 -0
- package/public/examples/todos.css +137 -0
- package/public/favicon.ico +0 -0
- package/public/log-dashboard.css +383 -0
- package/public/pulse-ui.css +2740 -0
- package/public/pulse-ui.js +236 -0
- package/public/pulse.css +149 -0
- package/scripts/build.js +411 -0
- package/src/agent/checklist.md +111 -0
- package/src/agent/coverage-check.js +66 -0
- package/src/agent/guide-components.md +274 -0
- package/src/agent/guide-examples.md +54 -0
- package/src/agent/guide-routing.md +36 -0
- package/src/agent/guide-server.md +258 -0
- package/src/agent/guide-spec.md +103 -0
- package/src/agent/guide-styles.md +191 -0
- package/src/agent/guide.md +979 -0
- package/src/agent/identity.md +106 -0
- package/src/agent/workflow.md +108 -0
- package/src/cli/cli.test.js +82 -0
- package/src/cli/dev.js +195 -0
- package/src/cli/discover.js +113 -0
- package/src/cli/index.js +361 -0
- package/src/cli/load-report.js +91 -0
- package/src/cli/load-runner.js +121 -0
- package/src/cli/report-server.js +723 -0
- package/src/cli/report.js +116 -0
- package/src/cli/scaffold.archive.js +1371 -0
- package/src/cli/scaffold.js +349 -0
- package/src/cli/start.js +74 -0
- package/src/html.js +19 -0
- package/src/mcp/server.js +884 -0
- package/src/mcp/validate-worker.js +110 -0
- package/src/runtime/image.js +74 -0
- package/src/runtime/image.test.js +111 -0
- package/src/runtime/index.js +621 -0
- package/src/runtime/navigate.js +146 -0
- package/src/runtime/runtime.test.js +773 -0
- package/src/runtime/ssr.js +464 -0
- package/src/runtime/ssr.test.js +421 -0
- package/src/runtime/store.js +92 -0
- package/src/runtime/toast.js +163 -0
- package/src/server/index.js +1386 -0
- package/src/server/server.test.js +1248 -0
- package/src/spec/schema.js +428 -0
- package/src/spec/schema.test.js +291 -0
- package/src/store/index.js +102 -0
- package/src/store/store.test.js +210 -0
- package/src/testing/html.js +283 -0
- package/src/testing/index.js +249 -0
- package/src/testing/testing.test.js +450 -0
- package/src/ui/accordion.js +28 -0
- package/src/ui/alert.js +43 -0
- package/src/ui/app-badge.js +48 -0
- package/src/ui/avatar.js +47 -0
- package/src/ui/badge.js +24 -0
- package/src/ui/banner.js +26 -0
- package/src/ui/breadcrumbs.js +38 -0
- package/src/ui/button.js +66 -0
- package/src/ui/card.js +34 -0
- package/src/ui/carousel.js +59 -0
- package/src/ui/charts.js +321 -0
- package/src/ui/checkbox.js +65 -0
- package/src/ui/cluster.js +44 -0
- package/src/ui/code-window.js +39 -0
- package/src/ui/container.js +24 -0
- package/src/ui/cta.js +37 -0
- package/src/ui/divider.js +29 -0
- package/src/ui/empty.js +33 -0
- package/src/ui/feature.js +33 -0
- package/src/ui/fieldset.js +37 -0
- package/src/ui/fileupload.js +89 -0
- package/src/ui/footer.js +38 -0
- package/src/ui/grid.js +36 -0
- package/src/ui/heading.js +45 -0
- package/src/ui/hero.js +37 -0
- package/src/ui/icons.js +161 -0
- package/src/ui/index.js +89 -0
- package/src/ui/input.js +74 -0
- package/src/ui/list.js +36 -0
- package/src/ui/media.js +44 -0
- package/src/ui/modal.js +80 -0
- package/src/ui/nav.js +61 -0
- package/src/ui/pricing.js +56 -0
- package/src/ui/progress.js +62 -0
- package/src/ui/prose.js +29 -0
- package/src/ui/pullquote.js +34 -0
- package/src/ui/radio.js +102 -0
- package/src/ui/rating.js +93 -0
- package/src/ui/search.js +77 -0
- package/src/ui/section.js +69 -0
- package/src/ui/segmented.js +50 -0
- package/src/ui/select.js +77 -0
- package/src/ui/slider.js +84 -0
- package/src/ui/spinner.js +34 -0
- package/src/ui/stack.js +36 -0
- package/src/ui/stat.js +52 -0
- package/src/ui/stepper.js +46 -0
- package/src/ui/switch.js +57 -0
- package/src/ui/table.js +45 -0
- package/src/ui/testimonial.js +48 -0
- package/src/ui/textarea.js +72 -0
- package/src/ui/timeline.js +72 -0
- package/src/ui/tooltip.js +28 -0
- package/src/ui/ui.test.js +1241 -0
- package/src/ui/uiimage.js +65 -0
- package/tsconfig.json +13 -0
- package/types/html.d.ts +17 -0
- package/types/image.d.ts +70 -0
- package/types/index.d.ts +7 -0
- package/types/navigate.d.ts +38 -0
- package/types/runtime.d.ts +63 -0
- package/types/schema.d.ts +243 -0
- package/types/server.d.ts +145 -0
- package/types/ssr.d.ts +110 -0
- package/types/testing.d.ts +154 -0
- package/types/ui.d.ts +704 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { pullquote } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/pullquote')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/pullquote',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Pullquote — Pulse Docs',
|
|
12
|
+
description: 'Styled blockquote component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/pullquote',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'pullquote',
|
|
21
|
+
description: 'Styled blockquote with an accent left border and optional attribution. Use it to highlight key quotes or testimonials within body content.',
|
|
22
|
+
content: `
|
|
23
|
+
|
|
24
|
+
<h2 class="doc-h2" id="basic">Basic</h2>
|
|
25
|
+
${demo(
|
|
26
|
+
pullquote({
|
|
27
|
+
quote: 'Design is not just what it looks like and feels like. Design is how it works.',
|
|
28
|
+
cite: 'Steve Jobs, co-founder of Apple',
|
|
29
|
+
}),
|
|
30
|
+
`pullquote({
|
|
31
|
+
quote: 'Design is not just what it looks like and feels like. Design is how it works.',
|
|
32
|
+
cite: 'Steve Jobs, co-founder of Apple',
|
|
33
|
+
})`
|
|
34
|
+
)}
|
|
35
|
+
|
|
36
|
+
<h2 class="doc-h2" id="large">Large</h2>
|
|
37
|
+
${demo(
|
|
38
|
+
pullquote({
|
|
39
|
+
quote: 'Simplicity is the ultimate sophistication.',
|
|
40
|
+
cite: 'Leonardo da Vinci',
|
|
41
|
+
size: 'lg',
|
|
42
|
+
}),
|
|
43
|
+
`pullquote({
|
|
44
|
+
quote: 'Simplicity is the ultimate sophistication.',
|
|
45
|
+
cite: 'Leonardo da Vinci',
|
|
46
|
+
size: 'lg',
|
|
47
|
+
})`
|
|
48
|
+
)}
|
|
49
|
+
|
|
50
|
+
<h2 class="doc-h2" id="no-cite">Without attribution</h2>
|
|
51
|
+
${demo(
|
|
52
|
+
pullquote({
|
|
53
|
+
quote: 'Good design makes a product understandable.',
|
|
54
|
+
}),
|
|
55
|
+
`pullquote({
|
|
56
|
+
quote: 'Good design makes a product understandable.',
|
|
57
|
+
})`
|
|
58
|
+
)}
|
|
59
|
+
|
|
60
|
+
${table(
|
|
61
|
+
['Prop', 'Type', 'Default', ''],
|
|
62
|
+
[
|
|
63
|
+
['<code>quote</code>', 'string', '—', 'The quote text — escaped automatically'],
|
|
64
|
+
['<code>cite</code>', 'string', '—', 'Attribution text — rendered in a <code><figcaption></code>'],
|
|
65
|
+
['<code>size</code>', '<code>md | lg</code>', '<code>md</code>', 'Controls the font size of the quote text'],
|
|
66
|
+
['<code>class</code>', 'string', '—', ''],
|
|
67
|
+
]
|
|
68
|
+
)}
|
|
69
|
+
`,
|
|
70
|
+
}),
|
|
71
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table, callout } from '../../lib/layout.js'
|
|
4
|
+
import { radio, radioGroup, fieldset } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/radio')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/radio',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Radio — Pulse Docs',
|
|
12
|
+
description: 'Radio button and radio group components for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/radio',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'radio',
|
|
21
|
+
description: 'Custom-styled radio buttons with full keyboard and screen-reader support. Use <code>radio()</code> for a single option or <code>radioGroup()</code> for a semantic group inside a form.',
|
|
22
|
+
content: `
|
|
23
|
+
|
|
24
|
+
<h2 class="doc-h2" id="group">Radio group</h2>
|
|
25
|
+
<p>The standard usage. <code>radioGroup()</code> renders a <code><fieldset></code> with a <code><legend></code> and marks the currently selected option via the <code>value</code> prop.</p>
|
|
26
|
+
${demo(
|
|
27
|
+
radioGroup({
|
|
28
|
+
name: 'plan',
|
|
29
|
+
legend: 'Plan',
|
|
30
|
+
value: 'pro',
|
|
31
|
+
options: [
|
|
32
|
+
{ value: 'starter', label: 'Starter' },
|
|
33
|
+
{ value: 'pro', label: 'Pro' },
|
|
34
|
+
{ value: 'team', label: 'Team' },
|
|
35
|
+
],
|
|
36
|
+
}),
|
|
37
|
+
`radioGroup({
|
|
38
|
+
name: 'plan',
|
|
39
|
+
legend: 'Plan',
|
|
40
|
+
value: state.plan, // marks the selected option
|
|
41
|
+
options: [
|
|
42
|
+
{ value: 'starter', label: 'Starter' },
|
|
43
|
+
{ value: 'pro', label: 'Pro' },
|
|
44
|
+
{ value: 'team', label: 'Team' },
|
|
45
|
+
],
|
|
46
|
+
})`,
|
|
47
|
+
{ col: true }
|
|
48
|
+
)}
|
|
49
|
+
|
|
50
|
+
<h2 class="doc-h2" id="hints">Per-option hints</h2>
|
|
51
|
+
<p>Each option accepts a <code>hint</code> string for supporting copy below the label.</p>
|
|
52
|
+
${demo(
|
|
53
|
+
radioGroup({
|
|
54
|
+
name: 'billing',
|
|
55
|
+
legend: 'Billing cycle',
|
|
56
|
+
value: 'annual',
|
|
57
|
+
options: [
|
|
58
|
+
{ value: 'monthly', label: 'Monthly', hint: 'Billed every month. Cancel any time.' },
|
|
59
|
+
{ value: 'annual', label: 'Annual', hint: 'Billed once a year. Save 20%.' },
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
`radioGroup({
|
|
63
|
+
name: 'billing',
|
|
64
|
+
legend: 'Billing cycle',
|
|
65
|
+
value: state.billing,
|
|
66
|
+
options: [
|
|
67
|
+
{ value: 'monthly', label: 'Monthly', hint: 'Billed every month. Cancel any time.' },
|
|
68
|
+
{ value: 'annual', label: 'Annual', hint: 'Billed once a year. Save 20%.' },
|
|
69
|
+
],
|
|
70
|
+
})`,
|
|
71
|
+
{ col: true }
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
<h2 class="doc-h2" id="error">Error state</h2>
|
|
75
|
+
<p>Pass <code>error</code> to show a validation message below the group. The message is linked via <code>aria-describedby</code>.</p>
|
|
76
|
+
${demo(
|
|
77
|
+
radioGroup({
|
|
78
|
+
name: 'size',
|
|
79
|
+
legend: 'Size',
|
|
80
|
+
error: 'Please select a size.',
|
|
81
|
+
options: [
|
|
82
|
+
{ value: 'sm', label: 'Small' },
|
|
83
|
+
{ value: 'md', label: 'Medium' },
|
|
84
|
+
{ value: 'lg', label: 'Large' },
|
|
85
|
+
],
|
|
86
|
+
}),
|
|
87
|
+
`radioGroup({
|
|
88
|
+
name: 'size',
|
|
89
|
+
legend: 'Size',
|
|
90
|
+
error: server.errors.size,
|
|
91
|
+
options: [
|
|
92
|
+
{ value: 'sm', label: 'Small' },
|
|
93
|
+
{ value: 'md', label: 'Medium' },
|
|
94
|
+
{ value: 'lg', label: 'Large' },
|
|
95
|
+
],
|
|
96
|
+
})`,
|
|
97
|
+
{ col: true }
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
<h2 class="doc-h2" id="disabled">Disabled options</h2>
|
|
101
|
+
<p>Set <code>disabled: true</code> on individual options, or pass <code>disabled</code> at the group level to disable all.</p>
|
|
102
|
+
${demo(
|
|
103
|
+
radioGroup({
|
|
104
|
+
name: 'tier',
|
|
105
|
+
legend: 'Tier',
|
|
106
|
+
value: 'basic',
|
|
107
|
+
options: [
|
|
108
|
+
{ value: 'basic', label: 'Basic' },
|
|
109
|
+
{ value: 'pro', label: 'Pro' },
|
|
110
|
+
{ value: 'enterprise', label: 'Enterprise', disabled: true },
|
|
111
|
+
],
|
|
112
|
+
}),
|
|
113
|
+
`radioGroup({
|
|
114
|
+
name: 'tier',
|
|
115
|
+
legend: 'Tier',
|
|
116
|
+
value: state.tier,
|
|
117
|
+
options: [
|
|
118
|
+
{ value: 'basic', label: 'Basic' },
|
|
119
|
+
{ value: 'pro', label: 'Pro' },
|
|
120
|
+
{ value: 'enterprise', label: 'Enterprise', disabled: true },
|
|
121
|
+
],
|
|
122
|
+
})`,
|
|
123
|
+
{ col: true }
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
<h2 class="doc-h2" id="gap">Gap</h2>
|
|
127
|
+
<p>Control spacing between options with <code>gap</code>: <code>'sm'</code> · <code>'md'</code> (default) · <code>'lg'</code>.</p>
|
|
128
|
+
${demo(
|
|
129
|
+
radioGroup({
|
|
130
|
+
name: 'color',
|
|
131
|
+
legend: 'Colour',
|
|
132
|
+
gap: 'lg',
|
|
133
|
+
value: 'blue',
|
|
134
|
+
options: [
|
|
135
|
+
{ value: 'red', label: 'Red' },
|
|
136
|
+
{ value: 'blue', label: 'Blue' },
|
|
137
|
+
{ value: 'green',label: 'Green'},
|
|
138
|
+
],
|
|
139
|
+
}),
|
|
140
|
+
`radioGroup({ ..., gap: 'lg' })`
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
<h2 class="doc-h2" id="single">Single radio</h2>
|
|
144
|
+
<p>Use <code>radio()</code> directly when you need to compose your own group layout — for example inside a <a href="/components/fieldset">fieldset</a> alongside other controls.</p>
|
|
145
|
+
${demo(
|
|
146
|
+
fieldset({
|
|
147
|
+
legend: 'Preferred contact',
|
|
148
|
+
content:
|
|
149
|
+
radio({ name: 'contact', value: 'email', label: 'Email', checked: true }) +
|
|
150
|
+
radio({ name: 'contact', value: 'phone', label: 'Phone' }),
|
|
151
|
+
}),
|
|
152
|
+
`fieldset({
|
|
153
|
+
legend: 'Preferred contact',
|
|
154
|
+
content:
|
|
155
|
+
radio({ name: 'contact', value: 'email', label: 'Email', checked: true }) +
|
|
156
|
+
radio({ name: 'contact', value: 'phone', label: 'Phone' }),
|
|
157
|
+
})`,
|
|
158
|
+
{ col: true }
|
|
159
|
+
)}
|
|
160
|
+
|
|
161
|
+
<h2 class="doc-h2" id="in-forms">In forms</h2>
|
|
162
|
+
${callout('note', 'Radio groups submit the selected <code>value</code> string under <code>name</code> in FormData. If nothing is selected, the field is absent from FormData. Read it in <code>action.onStart</code> or <code>action.run</code> via <code>formData.get(\'plan\')</code>.')}
|
|
163
|
+
|
|
164
|
+
<h2 class="doc-h2" id="props-group">radioGroup() props</h2>
|
|
165
|
+
${table(
|
|
166
|
+
['Prop', 'Type', 'Default', ''],
|
|
167
|
+
[
|
|
168
|
+
['<code>name</code>', 'string', '—', 'Shared <code>name</code> attribute for all inputs in the group'],
|
|
169
|
+
['<code>legend</code>', 'string', '—', 'Group label — renders as a <code><legend></code>'],
|
|
170
|
+
['<code>options</code>', 'array', '<code>[]</code>', 'Array of <code>{ value, label, hint?, disabled? }</code>'],
|
|
171
|
+
['<code>value</code>', 'string', '—', 'The currently selected value — marks the matching option as <code>checked</code>'],
|
|
172
|
+
['<code>hint</code>', 'string', '—', 'Helper text below the group'],
|
|
173
|
+
['<code>error</code>', 'string', '—', 'Validation error — linked via <code>aria-describedby</code>'],
|
|
174
|
+
['<code>gap</code>', '<code>sm | md | lg</code>', '<code>md</code>', 'Spacing between options'],
|
|
175
|
+
['<code>class</code>', 'string', '—', ''],
|
|
176
|
+
]
|
|
177
|
+
)}
|
|
178
|
+
|
|
179
|
+
<h2 class="doc-h2" id="props-radio">radio() props</h2>
|
|
180
|
+
${table(
|
|
181
|
+
['Prop', 'Type', 'Default', ''],
|
|
182
|
+
[
|
|
183
|
+
['<code>name</code>', 'string', '—', 'Field name'],
|
|
184
|
+
['<code>value</code>', 'string', '—', 'Submitted value when this option is selected'],
|
|
185
|
+
['<code>label</code>', 'string', '—', 'Visible label'],
|
|
186
|
+
['<code>checked</code>', 'boolean', 'false', ''],
|
|
187
|
+
['<code>disabled</code>', 'boolean', 'false', ''],
|
|
188
|
+
['<code>id</code>', 'string', '—', 'Override the generated <code>id</code>'],
|
|
189
|
+
['<code>class</code>', 'string', '—', ''],
|
|
190
|
+
]
|
|
191
|
+
)}
|
|
192
|
+
`,
|
|
193
|
+
}),
|
|
194
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table, callout } from '../../lib/layout.js'
|
|
4
|
+
import { rating, card } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/rating')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/rating',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Rating — Pulse Docs',
|
|
12
|
+
description: 'Star rating display and interactive input component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/rating',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'rating',
|
|
21
|
+
description: 'Star rating component with two modes. Without <code>name</code>: a read-only display that supports half-star values. With <code>name</code>: an interactive radio group that submits the selected value in FormData — no JavaScript required.',
|
|
22
|
+
content: `
|
|
23
|
+
|
|
24
|
+
<h2 class="doc-h2" id="display">Display (read-only)</h2>
|
|
25
|
+
<p>Omit <code>name</code> to render a read-only display. Pass <code>value</code> as a number — halves are supported.</p>
|
|
26
|
+
${demo(
|
|
27
|
+
`<div style="display:flex;flex-direction:column;gap:1rem;align-items:flex-start">` +
|
|
28
|
+
rating({ value: 5 }) +
|
|
29
|
+
rating({ value: 3.5 }) +
|
|
30
|
+
rating({ value: 2 }) +
|
|
31
|
+
rating({ value: 0 }) +
|
|
32
|
+
`</div>`,
|
|
33
|
+
`rating({ value: 5 }) // 5 stars
|
|
34
|
+
rating({ value: 3.5 }) // 3½ stars
|
|
35
|
+
rating({ value: 2 }) // 2 stars
|
|
36
|
+
rating({ value: 0 }) // empty`
|
|
37
|
+
)}
|
|
38
|
+
|
|
39
|
+
<h2 class="doc-h2" id="sizes">Sizes</h2>
|
|
40
|
+
<p><code>'sm'</code> · <code>'md'</code> (default) · <code>'lg'</code></p>
|
|
41
|
+
${demo(
|
|
42
|
+
`<div style="display:flex;flex-direction:column;gap:1rem;align-items:flex-start">` +
|
|
43
|
+
rating({ value: 4, size: 'sm' }) +
|
|
44
|
+
rating({ value: 4, size: 'md' }) +
|
|
45
|
+
rating({ value: 4, size: 'lg' }) +
|
|
46
|
+
`</div>`,
|
|
47
|
+
`rating({ value: 4, size: 'sm' })
|
|
48
|
+
rating({ value: 4, size: 'md' })
|
|
49
|
+
rating({ value: 4, size: 'lg' })`
|
|
50
|
+
)}
|
|
51
|
+
|
|
52
|
+
<h2 class="doc-h2" id="interactive">Interactive</h2>
|
|
53
|
+
<p>Add <code>name</code> to render radio inputs. Hovering a star previews the selection; clicking locks it. The selected value is submitted in FormData.</p>
|
|
54
|
+
${demo(
|
|
55
|
+
rating({ name: 'score', label: 'Your rating', value: 3, size: 'lg' }),
|
|
56
|
+
`rating({ name: 'score', label: 'Your rating', value: state.score, size: 'lg' })`
|
|
57
|
+
)}
|
|
58
|
+
|
|
59
|
+
<h2 class="doc-h2" id="in-card">In a card</h2>
|
|
60
|
+
${demo(
|
|
61
|
+
`<div style="max-width:320px">` +
|
|
62
|
+
card({
|
|
63
|
+
title: 'Noise-Cancelling Headphones',
|
|
64
|
+
content:
|
|
65
|
+
`<div style="display:flex;align-items:center;gap:.75rem;margin-bottom:.75rem">` +
|
|
66
|
+
rating({ value: 4.5 }) +
|
|
67
|
+
`<span style="color:var(--ui-muted);font-size:.85rem">4.5 · 1,284 reviews</span>` +
|
|
68
|
+
`</div>` +
|
|
69
|
+
`<p style="color:var(--ui-muted);margin:0;font-size:.9rem">Crystal-clear audio with 40-hour battery life and adaptive noise cancellation.</p>`,
|
|
70
|
+
}) +
|
|
71
|
+
`</div>`,
|
|
72
|
+
`card({
|
|
73
|
+
title: 'Noise-Cancelling Headphones',
|
|
74
|
+
content:
|
|
75
|
+
'<div style="display:flex;align-items:center;gap:.75rem">' +
|
|
76
|
+
rating({ value: 4.5 }) +
|
|
77
|
+
'<span>4.5 · 1,284 reviews</span>' +
|
|
78
|
+
'</div>' +
|
|
79
|
+
'<p>Crystal-clear audio...</p>',
|
|
80
|
+
})`
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
<h2 class="doc-h2" id="disabled">Disabled</h2>
|
|
84
|
+
${demo(
|
|
85
|
+
rating({ name: 'locked', value: 4, disabled: true }),
|
|
86
|
+
`rating({ name: 'locked', value: 4, disabled: true })`
|
|
87
|
+
)}
|
|
88
|
+
|
|
89
|
+
<h2 class="doc-h2" id="in-forms">In forms</h2>
|
|
90
|
+
${callout('note', 'The interactive rating submits the selected star count as a number string under <code>name</code> in FormData. Read it with <code>Number(formData.get(\'score\'))</code>. If nothing is selected, the field is absent from FormData.')}
|
|
91
|
+
|
|
92
|
+
${table(
|
|
93
|
+
['Prop', 'Type', 'Default', ''],
|
|
94
|
+
[
|
|
95
|
+
['<code>value</code>', 'number', '0', 'Current rating. Supports 0.5 steps in display mode.'],
|
|
96
|
+
['<code>max</code>', 'number', '5', 'Total number of stars'],
|
|
97
|
+
['<code>name</code>', 'string', '—', 'Field name — enables interactive radio mode'],
|
|
98
|
+
['<code>label</code>', 'string', '—', 'Accessible group label (interactive mode only)'],
|
|
99
|
+
['<code>size</code>', '<code>sm | md | lg</code>', '<code>md</code>', '1rem / 1.5rem / 2rem'],
|
|
100
|
+
['<code>disabled</code>', 'boolean', 'false', 'Interactive mode only'],
|
|
101
|
+
['<code>class</code>', 'string', '—', ''],
|
|
102
|
+
]
|
|
103
|
+
)}
|
|
104
|
+
`,
|
|
105
|
+
}),
|
|
106
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { search } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/search')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/search',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Search — Pulse Docs',
|
|
12
|
+
description: 'Search input component with icon, debounce binding, and clear button.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/search',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'search',
|
|
21
|
+
description: 'A search input with a built-in icon and optional clear button. Handles the native browser cancel button, <code>data-event</code> binding, and debounce in one component. Use this instead of <code>input({ type: \'search\' })</code>.',
|
|
22
|
+
content: `
|
|
23
|
+
${demo(
|
|
24
|
+
search({ name: 'q', label: 'Search', placeholder: 'Search products…' }) +
|
|
25
|
+
search({ name: 'q2', label: 'Search', placeholder: 'Search…', value: 'lamp', clearEvent: 'clearSearch', labelHidden: true }),
|
|
26
|
+
`search({ name: 'q', label: 'Search', placeholder: 'Search products…' })
|
|
27
|
+
|
|
28
|
+
// With value, clear button, and hidden label:
|
|
29
|
+
search({
|
|
30
|
+
name: 'q',
|
|
31
|
+
label: 'Search',
|
|
32
|
+
labelHidden: true,
|
|
33
|
+
placeholder: 'Search…',
|
|
34
|
+
value: state.search,
|
|
35
|
+
event: 'input:setSearch',
|
|
36
|
+
debounce: 200,
|
|
37
|
+
clearEvent: 'clearSearch',
|
|
38
|
+
})`,
|
|
39
|
+
{ col: true }
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
${table(
|
|
43
|
+
['Prop', 'Type', 'Default', ''],
|
|
44
|
+
[
|
|
45
|
+
['<code>name</code>', 'string', '—', 'Field name and id base'],
|
|
46
|
+
['<code>label</code>', 'string', '—', 'Label text — always provide for accessibility'],
|
|
47
|
+
['<code>labelHidden</code>', 'boolean', 'false', 'Hides label visually; still read by screen readers'],
|
|
48
|
+
['<code>placeholder</code>', 'string', '—', ''],
|
|
49
|
+
['<code>value</code>', 'string', '—', 'Current value — controls clear button visibility'],
|
|
50
|
+
['<code>event</code>', 'string', '—', '<code>data-event</code> binding, e.g. <code>\'input:setSearch\'</code>'],
|
|
51
|
+
['<code>debounce</code>', 'number', '200', 'Debounce delay in ms — only applied when <code>event</code> is set'],
|
|
52
|
+
['<code>clearEvent</code>', 'string', '—', 'Click event for the × button — only shown when <code>value</code> is non-empty'],
|
|
53
|
+
['<code>disabled</code>', 'boolean', 'false', ''],
|
|
54
|
+
['<code>id</code>', 'string', '—', 'Override generated id'],
|
|
55
|
+
['<code>class</code>', 'string', '—', 'Extra classes on the wrapper'],
|
|
56
|
+
['<code>attrs</code>', 'object', '{}', 'Extra attributes on the <code><input></code>'],
|
|
57
|
+
]
|
|
58
|
+
)}
|
|
59
|
+
`,
|
|
60
|
+
}),
|
|
61
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { container, section as uiSection } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/section')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/section',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Section — Pulse Docs',
|
|
12
|
+
description: 'Vertical padding wrapper with optional background. Compose with container() for full-width background with constrained content.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/section',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'section',
|
|
21
|
+
description: 'Vertical padding wrapper with optional background. Compose with <code>container()</code> for full-width background with constrained content.',
|
|
22
|
+
content: `
|
|
23
|
+
${demo(
|
|
24
|
+
uiSection({
|
|
25
|
+
eyebrow: 'Why Pulse',
|
|
26
|
+
title: 'Built for speed.',
|
|
27
|
+
subtitle: 'Every page scores 100 on Lighthouse by design, not by optimisation.',
|
|
28
|
+
align: 'center',
|
|
29
|
+
variant: 'alt',
|
|
30
|
+
content: container({ size: 'md', content: '<p style="text-align:center;color:var(--ui-muted)">Content goes here</p>' }),
|
|
31
|
+
}) +
|
|
32
|
+
uiSection({ variant: 'dark', content: container({ size: 'md', content: '<p style="text-align:center;color:var(--ui-muted)">Dark background · no header</p>' }) }),
|
|
33
|
+
`section({
|
|
34
|
+
eyebrow: 'Why Pulse',
|
|
35
|
+
title: 'Built for speed.',
|
|
36
|
+
subtitle: 'Every page scores 100 on Lighthouse by design.',
|
|
37
|
+
align: 'center',
|
|
38
|
+
variant: 'alt',
|
|
39
|
+
content: container({ size: 'lg', content: featureGrid }),
|
|
40
|
+
})`
|
|
41
|
+
)}
|
|
42
|
+
|
|
43
|
+
${table(
|
|
44
|
+
['Prop', 'Type', 'Default', ''],
|
|
45
|
+
[
|
|
46
|
+
['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
|
|
47
|
+
['<code>variant</code>', 'string', "'default'", "'default' · 'alt' · 'dark'"],
|
|
48
|
+
['<code>padding</code>', 'string', "'md'", "'sm' · 'md' · 'lg'"],
|
|
49
|
+
['<code>id</code>', 'string', '—', 'Anchor id for nav links'],
|
|
50
|
+
['<code>eyebrow</code>', 'string', '—', 'Small label above the title'],
|
|
51
|
+
['<code>title</code>', 'string', '—', 'Section heading'],
|
|
52
|
+
['<code>level</code>', 'number', '2', 'Heading tag for the title (1–6). Visual style is unchanged.'],
|
|
53
|
+
['<code>subtitle</code>', 'string', '—', 'Supporting text beneath the heading'],
|
|
54
|
+
['<code>align</code>', 'string', "'left'", "'left' · 'center'"],
|
|
55
|
+
]
|
|
56
|
+
)}
|
|
57
|
+
`,
|
|
58
|
+
}),
|
|
59
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table, callout, section } from '../../lib/layout.js'
|
|
4
|
+
import { segmented, stack } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/segmented')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/segmented',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Segmented — Pulse Docs',
|
|
12
|
+
description: 'iOS-style segmented control component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/segmented',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'segmented',
|
|
21
|
+
description: 'iOS-style segmented control built from hidden radio inputs. The selected segment is highlighted via CSS — no JavaScript required.',
|
|
22
|
+
content: `
|
|
23
|
+
|
|
24
|
+
<h2 class="doc-h2" id="basic">Basic</h2>
|
|
25
|
+
${demo(
|
|
26
|
+
segmented({
|
|
27
|
+
name: 'period',
|
|
28
|
+
value: 'week',
|
|
29
|
+
options: [
|
|
30
|
+
{ value: 'day', label: 'Day' },
|
|
31
|
+
{ value: 'week', label: 'Week' },
|
|
32
|
+
{ value: 'month', label: 'Month' },
|
|
33
|
+
],
|
|
34
|
+
}),
|
|
35
|
+
`segmented({
|
|
36
|
+
name: 'period',
|
|
37
|
+
value: state.period,
|
|
38
|
+
options: [
|
|
39
|
+
{ value: 'day', label: 'Day' },
|
|
40
|
+
{ value: 'week', label: 'Week' },
|
|
41
|
+
{ value: 'month', label: 'Month' },
|
|
42
|
+
],
|
|
43
|
+
})`
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
<h2 class="doc-h2" id="sizes">Sizes</h2>
|
|
47
|
+
${demo(
|
|
48
|
+
stack({ gap: 'md', content:
|
|
49
|
+
segmented({
|
|
50
|
+
name: 'size-sm',
|
|
51
|
+
value: 'b',
|
|
52
|
+
size: 'sm',
|
|
53
|
+
options: [
|
|
54
|
+
{ value: 'a', label: 'Small' },
|
|
55
|
+
{ value: 'b', label: 'Sizes' },
|
|
56
|
+
{ value: 'c', label: 'Here' },
|
|
57
|
+
],
|
|
58
|
+
}) +
|
|
59
|
+
segmented({
|
|
60
|
+
name: 'size-md',
|
|
61
|
+
value: 'b',
|
|
62
|
+
size: 'md',
|
|
63
|
+
options: [
|
|
64
|
+
{ value: 'a', label: 'Medium' },
|
|
65
|
+
{ value: 'b', label: 'Default' },
|
|
66
|
+
{ value: 'c', label: 'Size' },
|
|
67
|
+
],
|
|
68
|
+
}) +
|
|
69
|
+
segmented({
|
|
70
|
+
name: 'size-lg',
|
|
71
|
+
value: 'b',
|
|
72
|
+
size: 'lg',
|
|
73
|
+
options: [
|
|
74
|
+
{ value: 'a', label: 'Large' },
|
|
75
|
+
{ value: 'b', label: 'Size' },
|
|
76
|
+
{ value: 'c', label: 'Here' },
|
|
77
|
+
],
|
|
78
|
+
}),
|
|
79
|
+
}),
|
|
80
|
+
`segmented({ name: 'view', value: 'b', size: 'sm', options: [...] })
|
|
81
|
+
segmented({ name: 'view', value: 'b', size: 'md', options: [...] })
|
|
82
|
+
segmented({ name: 'view', value: 'b', size: 'lg', options: [...] })`
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
<h2 class="doc-h2" id="ui-context">View toggle</h2>
|
|
86
|
+
<p>A common use case — toggling between Grid and List views.</p>
|
|
87
|
+
${demo(
|
|
88
|
+
segmented({
|
|
89
|
+
name: 'layout',
|
|
90
|
+
value: 'grid',
|
|
91
|
+
options: [
|
|
92
|
+
{ value: 'grid', label: 'Grid' },
|
|
93
|
+
{ value: 'list', label: 'List' },
|
|
94
|
+
],
|
|
95
|
+
}),
|
|
96
|
+
`segmented({
|
|
97
|
+
name: 'layout',
|
|
98
|
+
value: state.layout,
|
|
99
|
+
options: [
|
|
100
|
+
{ value: 'grid', label: 'Grid' },
|
|
101
|
+
{ value: 'list', label: 'List' },
|
|
102
|
+
],
|
|
103
|
+
})`
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
${callout('note', 'The segmented control submits the selected <code>value</code> string under <code>name</code> in FormData. Read it via <code>formData.get(\'period\')</code> in <code>action.onStart</code> or <code>action.run</code>.')}
|
|
107
|
+
|
|
108
|
+
${table(
|
|
109
|
+
['Prop', 'Type', 'Default', ''],
|
|
110
|
+
[
|
|
111
|
+
['<code>name</code>', 'string', '—', 'Field name — submitted in FormData'],
|
|
112
|
+
['<code>options</code>', 'array', '[]', 'Array of <code>{ value, label }</code>'],
|
|
113
|
+
['<code>value</code>', 'string', '—', 'The currently selected value'],
|
|
114
|
+
['<code>size</code>', '<code>sm | md | lg</code>', '<code>md</code>', ''],
|
|
115
|
+
['<code>disabled</code>', 'boolean', 'false', 'Disables all options'],
|
|
116
|
+
['<code>class</code>', 'string', '—', ''],
|
|
117
|
+
]
|
|
118
|
+
)}
|
|
119
|
+
`,
|
|
120
|
+
}),
|
|
121
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { select } from '../../../../src/ui/index.js'
|
|
4
|
+
|
|
5
|
+
const { prev, next } = prevNext('/components/select')
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
route: '/components/select',
|
|
9
|
+
meta: {
|
|
10
|
+
title: 'Select — Pulse Docs',
|
|
11
|
+
description: 'Select component for Pulse UI.',
|
|
12
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
13
|
+
},
|
|
14
|
+
state: {},
|
|
15
|
+
view: () => renderComponentPage({
|
|
16
|
+
currentHref: '/components/select',
|
|
17
|
+
prev,
|
|
18
|
+
next,
|
|
19
|
+
name: 'select',
|
|
20
|
+
description: 'Options can be plain strings or <code>{ value, label }</code> objects. Pass <code>value</code> to mark the current selection.',
|
|
21
|
+
content: `
|
|
22
|
+
${demo(
|
|
23
|
+
select({
|
|
24
|
+
name: 'role', label: 'Role',
|
|
25
|
+
options: ['Admin', 'Editor', 'Viewer'],
|
|
26
|
+
value: 'Editor',
|
|
27
|
+
}) +
|
|
28
|
+
select({
|
|
29
|
+
name: 'status', label: 'Status',
|
|
30
|
+
options: [{ value: 'active', label: 'Active' }, { value: 'paused', label: 'Paused' }],
|
|
31
|
+
error: 'Please select a status',
|
|
32
|
+
}),
|
|
33
|
+
`select({ name: 'role', label: 'Role', options: ['Admin', 'Editor', 'Viewer'], value: state.role })
|
|
34
|
+
select({
|
|
35
|
+
name: 'country',
|
|
36
|
+
label: 'Country',
|
|
37
|
+
options: [{ value: 'gb', label: 'United Kingdom' }, { value: 'us', label: 'United States' }],
|
|
38
|
+
value: state.country,
|
|
39
|
+
required: true,
|
|
40
|
+
})`,
|
|
41
|
+
{ col: true }
|
|
42
|
+
)}
|
|
43
|
+
`,
|
|
44
|
+
}),
|
|
45
|
+
}
|