diff --git a/.changeset/plain-base-class-derivations.md b/.changeset/plain-base-class-derivations.md new file mode 100644 index 00000000..2932c7a0 --- /dev/null +++ b/.changeset/plain-base-class-derivations.md @@ -0,0 +1,4 @@ +--- +'svforge': patch +--- +Simplify the single-use class expressions in Base Badge, Alert, and Card components while preserving reactive variants. diff --git a/graphify-out/graph.json b/graphify-out/graph.json index 22d53ab5..7fc82124 100644 --- a/graphify-out/graph.json +++ b/graphify-out/graph.json @@ -14684,7 +14684,7 @@ "label": "children()", "file_type": "code", "source_file": "packages/svforge/templates/base/src/lib/components/svforge/ui/Card.svelte", - "source_location": "L37", + "source_location": "L36", "_callable": true, "_origin": "ast" }, @@ -21932,6 +21932,22 @@ "source_location": "L19", "_origin": "ast" }, + { + "id": "tests_base_derived_classes_test", + "label": "base-derived-classes.test.ts", + "file_type": "code", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L1", + "_origin": "ast" + }, + { + "id": "tests_base_derived_classes_test_children", + "label": "children", + "file_type": "code", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L11", + "_origin": "ast" + }, { "id": "tests_base_vitest_test", "label": "base-vitest.test.ts", @@ -26363,6 +26379,15 @@ "source_location": "L1", "_origin": "ast" }, + { + "id": "changeset_plain_base_class_derivations", + "label": "plain-base-class-derivations.md", + "file_type": "document", + "node_kind": "page", + "source_file": ".changeset/plain-base-class-derivations.md", + "source_location": "L1", + "_origin": "ast" + }, { "id": "changeset_quiet_lib_imports", "label": "quiet-lib-imports.md", @@ -58825,7 +58850,7 @@ "relation": "contains", "confidence": "EXTRACTED", "source_file": "packages/svforge/templates/base/src/lib/components/svforge/ui/Card.svelte", - "source_location": "L37", + "source_location": "L36", "weight": 1, "_origin": "ast" }, @@ -74144,6 +74169,82 @@ "weight": 1, "_origin": "ast" }, + { + "source": "tests_base_derived_classes_test", + "target": "ref_vitest", + "relation": "imports_from", + "context": "import", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L2", + "weight": 1, + "_origin": "ast" + }, + { + "source": "tests_base_derived_classes_test", + "target": "ref_testing_library_svelte", + "relation": "imports_from", + "context": "import", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L3", + "weight": 1, + "_origin": "ast" + }, + { + "source": "tests_base_derived_classes_test", + "target": "ref_svelte", + "relation": "imports_from", + "context": "import", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L4", + "weight": 1, + "_origin": "ast" + }, + { + "source": "tests_base_derived_classes_test", + "target": "packages_svforge_templates_base_src_lib_components_svforge_ui_alert", + "relation": "imports_from", + "context": "import", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L5", + "weight": 1, + "_origin": "ast" + }, + { + "source": "tests_base_derived_classes_test", + "target": "packages_svforge_templates_base_src_lib_components_svforge_primitives_badge", + "relation": "imports_from", + "context": "import", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L6", + "weight": 1, + "_origin": "ast" + }, + { + "source": "tests_base_derived_classes_test", + "target": "packages_svforge_templates_base_src_lib_components_svforge_ui_card", + "relation": "imports_from", + "context": "import", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L7", + "weight": 1, + "_origin": "ast" + }, + { + "source": "tests_base_derived_classes_test", + "target": "tests_base_derived_classes_test_children", + "relation": "contains", + "confidence": "EXTRACTED", + "source_file": "tests/base-derived-classes.test.ts", + "source_location": "L11", + "weight": 1, + "_origin": "ast" + }, { "source": "tests_base_vitest_test", "target": "ref_vitest", diff --git a/graphify-out/manifest.json b/graphify-out/manifest.json index adf3fd2f..85cdd71a 100644 --- a/graphify-out/manifest.json +++ b/graphify-out/manifest.json @@ -488,7 +488,7 @@ "semantic_hash": "" }, "packages/svforge/src/templates.ts": { - "ast_hash": "45944ccabfdddddea22cb9f895bd8399", + "ast_hash": "3136b546212ced01f45ec40895b95434", "semantic_hash": "" }, "packages/svforge/src/upgrade.ts": { @@ -572,7 +572,7 @@ "semantic_hash": "" }, "packages/svforge/templates/base/src/lib/components/svforge/primitives/Badge.svelte": { - "ast_hash": "1770661d69922df5e4ecffbc64116c7b", + "ast_hash": "2e4c9fc1f776ce4179de9f01c2fcb8c1", "semantic_hash": "" }, "packages/svforge/templates/base/src/lib/components/svforge/primitives/Button.svelte": { @@ -608,11 +608,11 @@ "semantic_hash": "" }, "packages/svforge/templates/base/src/lib/components/svforge/ui/Alert.svelte": { - "ast_hash": "1ed6de77dc0081213f774263a143f432", + "ast_hash": "f76f5ea141808a7bbdbdf68026e0cd69", "semantic_hash": "" }, "packages/svforge/templates/base/src/lib/components/svforge/ui/Card.svelte": { - "ast_hash": "4254dfefc2fbc9f17dd1a41f15642544", + "ast_hash": "01a989ccc1b348ca5f8bc4271fce47c4", "semantic_hash": "" }, "packages/svforge/templates/base/src/lib/components/svforge/ui/Logo.svelte": { @@ -2102,5 +2102,13 @@ ".changeset/quiet-lib-imports.md": { "ast_hash": "c5f0c8287e0323fa95e78b17fae6eff4", "semantic_hash": "" + }, + "tests/base-derived-classes.test.ts": { + "ast_hash": "3d63aa4e9d6cf63788d92d839776270b", + "semantic_hash": "" + }, + ".changeset/plain-base-class-derivations.md": { + "ast_hash": "7778c6938a8841c5a60545fb9b168b42", + "semantic_hash": "" } } diff --git a/packages/svforge/src/templates.ts b/packages/svforge/src/templates.ts index f1921324..58de035b 100644 --- a/packages/svforge/src/templates.ts +++ b/packages/svforge/src/templates.ts @@ -10,7 +10,7 @@ export const baseFiles = { "/lib/components/svforge/layout/Footer.svelte": "\n\n\n", "/lib/components/svforge/layout/Navbar.svelte": "\n\n\n", "/lib/components/svforge/layout/index.ts": "export { default as AuthLayout } from './AuthLayout.svelte';\nexport { default as Footer } from './Footer.svelte';\nexport { default as Navbar } from './Navbar.svelte';\n", - "/lib/components/svforge/primitives/Badge.svelte": "\n\n\n\t{@render children()}\n\n", + "/lib/components/svforge/primitives/Badge.svelte": "\n\n\n\t{@render children()}\n\n", "/lib/components/svforge/primitives/Button.svelte": "\n\n{#snippet spinner()}\n\t\n\t{loadingLabel}\n{/snippet}\n\n{#if isAnchor}\n\t\n\t\n\t\n\t\t{#if loading}\n\t\t\t{@render spinner()}\n\t\t{/if}\n\t\t{@render children()}\n\t\n{:else}\n\t\n\t\n\t\t{#if loading}\n\t\t\t{@render spinner()}\n\t\t{/if}\n\t\t{@render children()}\n\t\n{/if}\n", "/lib/components/svforge/primitives/Checkbox.svelte": "\n\n\n", "/lib/components/svforge/primitives/Input.svelte": "\n\n
\n\t{#if label}\n\t\t\n\t{/if}\n\t
\n\t\t\n\t\t{#if trailing}\n\t\t\t
\n\t\t\t\t{@render trailing()}\n\t\t\t
\n\t\t{/if}\n\t
\n\t{#if hasError}\n\t\t

{error}

\n\t{/if}\n
\n", @@ -19,8 +19,8 @@ export const baseFiles = { "/lib/components/svforge/primitives/Toggle.svelte": "\n\n\n\t\n\t\t\n\t\n\t{#if label}\n\t\t{label}\n\t{/if}\n\t\n\n", "/lib/components/svforge/primitives/index.ts": "export { default as Badge } from './Badge.svelte';\nexport { default as Button } from './Button.svelte';\nexport { default as Checkbox } from './Checkbox.svelte';\nexport { default as Input } from './Input.svelte';\nexport { default as Select } from './Select.svelte';\nexport { default as Textarea } from './Textarea.svelte';\nexport { default as Toggle } from './Toggle.svelte';\n", "/lib/components/svforge/primitives/types.ts": "/**\n * Plain-TypeScript prop contracts for the primitives (#321).\n *\n * Living in a .ts module — instead of inline in the .svelte files — keeps the\n * discriminated unions and generics importable by type tests (`vitest\n * --typecheck`) and by consumers, without going through Svelte tooling.\n */\nimport type { Snippet } from 'svelte';\nimport type {\n\tHTMLAnchorAttributes,\n\tHTMLButtonAttributes,\n\tHTMLTableAttributes\n} from 'svelte/elements';\n\n// ── Button ───────────────────────────────────────────────────────────────\n\nexport type ButtonVariant = 'filled' | 'outlined' | 'tonal' | 'ghost';\nexport type ButtonColor =\n\t| 'primary'\n\t| 'secondary'\n\t| 'tertiary'\n\t| 'success'\n\t| 'warning'\n\t| 'error'\n\t| 'surface';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\n/** Visual + ergonomic props shared by both branches of the union. */\nexport interface ButtonSharedProps {\n\tvariant?: ButtonVariant;\n\tcolor?: ButtonColor;\n\tsize?: ButtonSize;\n\t/**\n\t * Visual disabled state. The button branch renders a native `disabled`\n\t * attribute; the anchor branch degrades to `aria-disabled` + reduced\n\t * styling — anchors have no native disabled semantics (#321).\n\t */\n\tdisabled?: boolean;\n\tloading?: boolean;\n\t/**\n\t * Text announced to screen readers while `loading` is active — the visual\n\t * spinner is aria-hidden. Defaults to the localized `common_loading` copy.\n\t */\n\tloadingLabel?: string;\n\tclass?: string;\n\tchildren: Snippet;\n}\n\n/**\n * Button branch — rendered when `href` is absent OR an empty string (the\n * runtime discriminant, mirrored exactly). Anchor-only attributes (`target`,\n * `rel`) are rejected here: TypeScript enforces that each branch only carries\n * its own element's attributes (#321). `href` admits `''` so a dynamically\n * computed empty href keeps full button semantics (`type`, `name`, `form`…)\n * instead of being mis-typed into the anchor branch.\n */\nexport type ButtonProps = ButtonSharedProps &\n\tOmit & {\n\t\thref?: '' | undefined;\n\t\ttype?: 'button' | 'submit' | 'reset';\n\t};\n\n/**\n * Anchor branch — rendered when `href` is a non-empty string. Button-only\n * semantics (`type`, `name`, `form`) are rejected: a link styled as a button\n * is still a link, and a disabled/loading one keeps anchor semantics via\n * `aria-disabled` instead of falling back to a `