@surfnet/curve-angular 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/.postcssrc.json +7 -0
  2. package/.storybook/main.ts +9 -0
  3. package/.storybook/manager.ts +3 -0
  4. package/.storybook/preview.ts +28 -0
  5. package/.storybook/tsconfig.json +8 -0
  6. package/.turbo/turbo-build.log +32 -0
  7. package/CHANGELOG.md +29 -0
  8. package/angular.json +52 -0
  9. package/components.json +5 -0
  10. package/dist/fesm2022/surfnet-curve-angular.mjs +4237 -0
  11. package/dist/fesm2022/surfnet-curve-angular.mjs.map +1 -0
  12. package/dist/files/geist-cyrillic-ext-wght-italic.woff2 +0 -0
  13. package/dist/files/geist-cyrillic-ext-wght-normal.woff2 +0 -0
  14. package/dist/files/geist-cyrillic-wght-italic.woff2 +0 -0
  15. package/dist/files/geist-cyrillic-wght-normal.woff2 +0 -0
  16. package/dist/files/geist-latin-ext-wght-italic.woff2 +0 -0
  17. package/dist/files/geist-latin-ext-wght-normal.woff2 +0 -0
  18. package/dist/files/geist-latin-wght-italic.woff2 +0 -0
  19. package/dist/files/geist-latin-wght-normal.woff2 +0 -0
  20. package/dist/files/geist-vietnamese-wght-italic.woff2 +0 -0
  21. package/dist/files/geist-vietnamese-wght-normal.woff2 +0 -0
  22. package/dist/styles.css +2 -0
  23. package/dist/types/surfnet-curve-angular.d.ts +1017 -0
  24. package/ng-package.json +20 -0
  25. package/package.json +73 -0
  26. package/scripts/copy-font-files.ts +15 -0
  27. package/scripts/rewrite-helm-imports.ts +60 -0
  28. package/src/foundations/design-tokens.stories.ts +198 -0
  29. package/src/lib/ui/avatar/src/index.ts +22 -0
  30. package/src/lib/ui/avatar/src/lib/hlm-avatar-badge.ts +19 -0
  31. package/src/lib/ui/avatar/src/lib/hlm-avatar-fallback.ts +20 -0
  32. package/src/lib/ui/avatar/src/lib/hlm-avatar-group-count.ts +17 -0
  33. package/src/lib/ui/avatar/src/lib/hlm-avatar-group.ts +17 -0
  34. package/src/lib/ui/avatar/src/lib/hlm-avatar-image.ts +19 -0
  35. package/src/lib/ui/avatar/src/lib/hlm-avatar.stories.ts +107 -0
  36. package/src/lib/ui/avatar/src/lib/hlm-avatar.ts +32 -0
  37. package/src/lib/ui/breadcrumb/src/index.ts +30 -0
  38. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-ellipsis.ts +36 -0
  39. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-item.ts +14 -0
  40. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-link.ts +36 -0
  41. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-list.ts +17 -0
  42. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-page.ts +17 -0
  43. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-separator.ts +27 -0
  44. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb.stories.ts +81 -0
  45. package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb.ts +13 -0
  46. package/src/lib/ui/button/src/index.ts +6 -0
  47. package/src/lib/ui/button/src/lib/hlm-button.stories.ts +170 -0
  48. package/src/lib/ui/button/src/lib/hlm-button.token.ts +22 -0
  49. package/src/lib/ui/button/src/lib/hlm-button.ts +76 -0
  50. package/src/lib/ui/card/src/index.ts +25 -0
  51. package/src/lib/ui/card/src/lib/hlm-card-action.ts +12 -0
  52. package/src/lib/ui/card/src/lib/hlm-card-content.ts +12 -0
  53. package/src/lib/ui/card/src/lib/hlm-card-description.ts +12 -0
  54. package/src/lib/ui/card/src/lib/hlm-card-footer.ts +14 -0
  55. package/src/lib/ui/card/src/lib/hlm-card-header.ts +15 -0
  56. package/src/lib/ui/card/src/lib/hlm-card-title.ts +12 -0
  57. package/src/lib/ui/card/src/lib/hlm-card.stories.ts +105 -0
  58. package/src/lib/ui/card/src/lib/hlm-card.token.ts +19 -0
  59. package/src/lib/ui/card/src/lib/hlm-card.ts +22 -0
  60. package/src/lib/ui/checkbox/src/index.ts +5 -0
  61. package/src/lib/ui/checkbox/src/lib/hlm-checkbox.stories.ts +78 -0
  62. package/src/lib/ui/checkbox/src/lib/hlm-checkbox.ts +160 -0
  63. package/src/lib/ui/data-table/src/index.ts +14 -0
  64. package/src/lib/ui/data-table/src/lib/hlm-data-table-content.ts +87 -0
  65. package/src/lib/ui/data-table/src/lib/hlm-data-table-pagination.ts +62 -0
  66. package/src/lib/ui/data-table/src/lib/hlm-data-table-toolbar.ts +16 -0
  67. package/src/lib/ui/data-table/src/lib/hlm-data-table.stories.ts +479 -0
  68. package/src/lib/ui/data-table/src/lib/inject-data-table.ts +86 -0
  69. package/src/lib/ui/dropdown-menu/src/index.ts +45 -0
  70. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-checkbox-indicator.ts +20 -0
  71. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-checkbox.ts +36 -0
  72. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-group.ts +16 -0
  73. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-item-sub-indicator.ts +17 -0
  74. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-item.ts +41 -0
  75. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-label.ts +20 -0
  76. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-radio-indicator.ts +20 -0
  77. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-radio.ts +36 -0
  78. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-separator.ts +14 -0
  79. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-shortcut.ts +14 -0
  80. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-sub.ts +58 -0
  81. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-token.ts +26 -0
  82. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-trigger.ts +49 -0
  83. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu.stories.ts +125 -0
  84. package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu.ts +62 -0
  85. package/src/lib/ui/field/src/index.ts +34 -0
  86. package/src/lib/ui/field/src/lib/hlm-field-content.ts +12 -0
  87. package/src/lib/ui/field/src/lib/hlm-field-description.ts +53 -0
  88. package/src/lib/ui/field/src/lib/hlm-field-error.ts +100 -0
  89. package/src/lib/ui/field/src/lib/hlm-field-group.ts +15 -0
  90. package/src/lib/ui/field/src/lib/hlm-field-label.ts +17 -0
  91. package/src/lib/ui/field/src/lib/hlm-field-legend.ts +17 -0
  92. package/src/lib/ui/field/src/lib/hlm-field-separator.ts +24 -0
  93. package/src/lib/ui/field/src/lib/hlm-field-set.ts +15 -0
  94. package/src/lib/ui/field/src/lib/hlm-field-title.ts +15 -0
  95. package/src/lib/ui/field/src/lib/hlm-field.stories.ts +117 -0
  96. package/src/lib/ui/field/src/lib/hlm-field.ts +49 -0
  97. package/src/lib/ui/icon/src/index.ts +7 -0
  98. package/src/lib/ui/icon/src/lib/hlm-icon.token.ts +20 -0
  99. package/src/lib/ui/icon/src/lib/hlm-icon.ts +42 -0
  100. package/src/lib/ui/input/src/index.ts +5 -0
  101. package/src/lib/ui/input/src/lib/hlm-input.stories.ts +94 -0
  102. package/src/lib/ui/input/src/lib/hlm-input.ts +20 -0
  103. package/src/lib/ui/input-group/src/index.ts +22 -0
  104. package/src/lib/ui/input-group/src/lib/hlm-input-group-addon.ts +40 -0
  105. package/src/lib/ui/input-group/src/lib/hlm-input-group-button.ts +47 -0
  106. package/src/lib/ui/input-group/src/lib/hlm-input-group-input.ts +17 -0
  107. package/src/lib/ui/input-group/src/lib/hlm-input-group-text.ts +14 -0
  108. package/src/lib/ui/input-group/src/lib/hlm-input-group-textarea.ts +17 -0
  109. package/src/lib/ui/input-group/src/lib/hlm-input-group.stories.ts +133 -0
  110. package/src/lib/ui/input-group/src/lib/hlm-input-group.ts +18 -0
  111. package/src/lib/ui/label/src/index.ts +5 -0
  112. package/src/lib/ui/label/src/lib/hlm-label.stories.ts +55 -0
  113. package/src/lib/ui/label/src/lib/hlm-label.ts +17 -0
  114. package/src/lib/ui/select/src/index.ts +52 -0
  115. package/src/lib/ui/select/src/lib/hlm-select-content.ts +44 -0
  116. package/src/lib/ui/select/src/lib/hlm-select-group.ts +14 -0
  117. package/src/lib/ui/select/src/lib/hlm-select-item.ts +36 -0
  118. package/src/lib/ui/select/src/lib/hlm-select-label.ts +14 -0
  119. package/src/lib/ui/select/src/lib/hlm-select-multiple.ts +45 -0
  120. package/src/lib/ui/select/src/lib/hlm-select-placeholder.ts +17 -0
  121. package/src/lib/ui/select/src/lib/hlm-select-portal.ts +8 -0
  122. package/src/lib/ui/select/src/lib/hlm-select-scroll-down.ts +22 -0
  123. package/src/lib/ui/select/src/lib/hlm-select-scroll-up.ts +22 -0
  124. package/src/lib/ui/select/src/lib/hlm-select-separator.ts +14 -0
  125. package/src/lib/ui/select/src/lib/hlm-select-trigger.ts +59 -0
  126. package/src/lib/ui/select/src/lib/hlm-select-value-template.ts +5 -0
  127. package/src/lib/ui/select/src/lib/hlm-select-value.ts +18 -0
  128. package/src/lib/ui/select/src/lib/hlm-select-values-content.ts +9 -0
  129. package/src/lib/ui/select/src/lib/hlm-select-values.ts +5 -0
  130. package/src/lib/ui/select/src/lib/hlm-select.stories.ts +105 -0
  131. package/src/lib/ui/select/src/lib/hlm-select.ts +45 -0
  132. package/src/lib/ui/separator/src/index.ts +5 -0
  133. package/src/lib/ui/separator/src/lib/hlm-separator.stories.ts +88 -0
  134. package/src/lib/ui/separator/src/lib/hlm-separator.ts +19 -0
  135. package/src/lib/ui/sheet/src/index.ts +34 -0
  136. package/src/lib/ui/sheet/src/lib/hlm-sheet-close.ts +9 -0
  137. package/src/lib/ui/sheet/src/lib/hlm-sheet-content.ts +66 -0
  138. package/src/lib/ui/sheet/src/lib/hlm-sheet-description.ts +14 -0
  139. package/src/lib/ui/sheet/src/lib/hlm-sheet-footer.ts +12 -0
  140. package/src/lib/ui/sheet/src/lib/hlm-sheet-header.ts +12 -0
  141. package/src/lib/ui/sheet/src/lib/hlm-sheet-overlay.ts +27 -0
  142. package/src/lib/ui/sheet/src/lib/hlm-sheet-portal.ts +8 -0
  143. package/src/lib/ui/sheet/src/lib/hlm-sheet-title.ts +14 -0
  144. package/src/lib/ui/sheet/src/lib/hlm-sheet-trigger.ts +9 -0
  145. package/src/lib/ui/sheet/src/lib/hlm-sheet.ts +29 -0
  146. package/src/lib/ui/sidebar/src/index.ts +75 -0
  147. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-content.ts +18 -0
  148. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-footer.ts +15 -0
  149. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group-action.ts +18 -0
  150. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group-content.ts +15 -0
  151. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group-label.ts +18 -0
  152. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group.ts +15 -0
  153. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-header.ts +15 -0
  154. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-input.ts +17 -0
  155. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-inset.ts +15 -0
  156. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-action.ts +24 -0
  157. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-badge.ts +18 -0
  158. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-button.ts +97 -0
  159. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-item.ts +15 -0
  160. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-skeleton.ts +33 -0
  161. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-sub-button.ts +41 -0
  162. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-sub-item.ts +15 -0
  163. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-sub.ts +18 -0
  164. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu.ts +15 -0
  165. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-rail.ts +34 -0
  166. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-separator.ts +17 -0
  167. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-trigger.ts +35 -0
  168. package/src/lib/ui/sidebar/src/lib/hlm-sidebar-wrapper.ts +24 -0
  169. package/src/lib/ui/sidebar/src/lib/hlm-sidebar.service.ts +136 -0
  170. package/src/lib/ui/sidebar/src/lib/hlm-sidebar.stories.ts +177 -0
  171. package/src/lib/ui/sidebar/src/lib/hlm-sidebar.token.ts +33 -0
  172. package/src/lib/ui/sidebar/src/lib/hlm-sidebar.ts +139 -0
  173. package/src/lib/ui/skeleton/src/index.ts +5 -0
  174. package/src/lib/ui/skeleton/src/lib/hlm-skeleton.ts +14 -0
  175. package/src/lib/ui/table/src/index.ts +25 -0
  176. package/src/lib/ui/table/src/lib/hlm-table.stories.ts +70 -0
  177. package/src/lib/ui/table/src/lib/hlm-table.ts +129 -0
  178. package/src/lib/ui/textarea/src/index.ts +5 -0
  179. package/src/lib/ui/textarea/src/lib/hlm-textarea.stories.ts +72 -0
  180. package/src/lib/ui/textarea/src/lib/hlm-textarea.ts +21 -0
  181. package/src/lib/ui/tooltip/src/index.ts +5 -0
  182. package/src/lib/ui/tooltip/src/lib/hlm-tooltip.ts +44 -0
  183. package/src/lib/ui/utils/src/index.ts +1 -0
  184. package/src/lib/ui/utils/src/lib/hlm.ts +313 -0
  185. package/src/public-api.ts +24 -0
  186. package/src/styles.css +43 -0
  187. package/tsconfig.json +45 -0
  188. package/tsconfig.lib.json +11 -0
  189. package/tsconfig.lib.prod.json +9 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "dist",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ },
7
+ "allowedNonPeerDependencies": [
8
+ "@angular/cdk",
9
+ "@angular/forms",
10
+ "@angular/router",
11
+ "@fontsource-variable/geist",
12
+ "@ng-icons/phosphor-icons",
13
+ "@spartan-ng/brain",
14
+ "@tanstack/angular-table",
15
+ "class-variance-authority",
16
+ "clsx",
17
+ "tailwind-merge",
18
+ "tslib"
19
+ ]
20
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@surfnet/curve-angular",
3
+ "version": "0.2.0",
4
+ "sideEffects": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "module": "./dist/fesm2022/surfnet-curve-angular.mjs",
9
+ "types": "./dist/types/surfnet-curve-angular.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/types/surfnet-curve-angular.d.ts",
13
+ "default": "./dist/fesm2022/surfnet-curve-angular.mjs"
14
+ },
15
+ "./styles.css": "./dist/styles.css"
16
+ },
17
+ "peerDependencies": {
18
+ "@angular/common": "^22.0.0",
19
+ "@angular/core": "^22.0.0",
20
+ "@ng-icons/core": "^33.0.0",
21
+ "rxjs": "^7.8.0"
22
+ },
23
+ "dependencies": {
24
+ "@angular/cdk": "22.0.0",
25
+ "@angular/forms": ">=21.0.0 <23.0.0",
26
+ "@angular/router": ">=21.0.0 <23.0.0",
27
+ "@fontsource-variable/geist": "5.2.9",
28
+ "@ng-icons/phosphor-icons": ">=32.0.0 <34.0.0",
29
+ "@spartan-ng/brain": "0.0.1-alpha.715",
30
+ "@tanstack/angular-table": "^8.21.4",
31
+ "class-variance-authority": "0.7.1",
32
+ "clsx": "2.1.1",
33
+ "tailwind-merge": "3.6.0",
34
+ "tslib": "2.8.1"
35
+ },
36
+ "devDependencies": {
37
+ "@angular/build": "22.0.2",
38
+ "@angular/cli": "22.0.2",
39
+ "@angular/common": "22.0.2",
40
+ "@angular/compiler": "22.0.2",
41
+ "@angular/compiler-cli": "22.0.2",
42
+ "@angular/core": "22.0.2",
43
+ "@angular/platform-browser": "22.0.2",
44
+ "@ng-icons/core": "33.3.0",
45
+ "@spartan-ng/cli": "0.0.1-alpha.715",
46
+ "@storybook/addon-a11y": "10.4.5",
47
+ "@storybook/addon-docs": "10.4.5",
48
+ "@storybook/angular": "10.4.5",
49
+ "@tailwindcss/cli": "4.3.1",
50
+ "@tailwindcss/postcss": "4.3.1",
51
+ "jiti": "2.7.0",
52
+ "ng-packagr": "22.0.0",
53
+ "rxjs": "7.8.2",
54
+ "storybook": "10.4.5",
55
+ "tailwindcss": "4.3.1",
56
+ "tw-animate-css": "1.4.0",
57
+ "typescript": "6.0.3",
58
+ "zone.js": "0.16.2",
59
+ "@surfnet/curve-contracts": "0.2.0",
60
+ "@surfnet/curve-storybook-config": "0.2.0",
61
+ "@surfnet/curve-tokens": "0.2.0",
62
+ "@surfnet/curve-typescript-config": "0.2.0"
63
+ },
64
+ "scripts": {
65
+ "build": "ng build angular && pnpm build:css",
66
+ "build:css": "tailwindcss -i ./src/styles.css -o ./dist/styles.css --minify && jiti scripts/copy-font-files.ts",
67
+ "dev": "ng build angular --watch --configuration development",
68
+ "lint": "ngc --noEmit -p tsconfig.json",
69
+ "storybook": "ng run angular:storybook",
70
+ "build-storybook": "ng run angular:build-storybook",
71
+ "fix-helm-imports": "jiti scripts/rewrite-helm-imports.ts"
72
+ }
73
+ }
@@ -0,0 +1,15 @@
1
+ // Cross-platform replacement for `rm -rf` + `cp -RL` (fail on Windows).
2
+ // dereference: true is needed since pnpm symlinks node_modules packages.
3
+
4
+ import { cpSync, rmSync } from 'node:fs';
5
+ import { dirname, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
9
+ const source = join(packageRoot, 'node_modules/@fontsource-variable/geist/files');
10
+ const destination = join(packageRoot, 'dist/files');
11
+
12
+ rmSync(destination, { recursive: true, force: true });
13
+ cpSync(source, destination, { recursive: true, dereference: true });
14
+
15
+ console.log(`Copied font files to ${join('dist', 'files')}.`);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Rewrites `@spartan-ng/helm/<name>` imports to relative paths.
3
+ *
4
+ * The Spartan CLI always vendors cross-component references through the
5
+ * `@spartan-ng/helm/<name>` tsconfig path alias. That alias only resolves at
6
+ * build time in a consuming *app* (whose own bundler reads tsconfig `paths`).
7
+ * `@surfnet/curve-angular` instead builds itself into a redistributable package via
8
+ * ng-packagr, which does not consult tsconfig `paths` and leaves the alias as
9
+ * an unresolved external import in the published bundle. Run this after every
10
+ * `ng g @spartan-ng/cli:ui <component>` to convert the new alias imports to
11
+ * relative ones so ng-packagr inlines them correctly.
12
+ *
13
+ * Usage: pnpm --filter @surfnet/curve-angular fix-helm-imports
14
+ */
15
+
16
+ import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
17
+ import { dirname, join, relative } from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
21
+ const srcRoot = join(packageRoot, 'src');
22
+ const importPattern = /from (['"])@spartan-ng\/helm\/([a-z0-9-]+)\1/g;
23
+
24
+ function walk(dir: string, files: string[] = []): string[] {
25
+ for (const entry of readdirSync(dir)) {
26
+ const full = join(dir, entry);
27
+ if (statSync(full).isDirectory()) {
28
+ walk(full, files);
29
+ } else if (entry.endsWith('.ts')) {
30
+ files.push(full);
31
+ }
32
+ }
33
+ return files;
34
+ }
35
+
36
+ function toRelativeSpecifier(fromFile: string, name: string): string {
37
+ const targetDir = join(srcRoot, 'lib', 'ui', name, 'src');
38
+ let rel = relative(dirname(fromFile), targetDir).replace(/\\/g, '/');
39
+ if (!rel.startsWith('.')) rel = `./${rel}`;
40
+ return rel;
41
+ }
42
+
43
+ let changedFiles = 0;
44
+ let changedImports = 0;
45
+
46
+ for (const file of walk(srcRoot)) {
47
+ const original = readFileSync(file, 'utf8');
48
+ let fileChanged = false;
49
+ const updated = original.replace(importPattern, (_match, quote: string, name: string) => {
50
+ fileChanged = true;
51
+ changedImports++;
52
+ return `from ${quote}${toRelativeSpecifier(file, name)}${quote}`;
53
+ });
54
+ if (fileChanged) {
55
+ writeFileSync(file, updated);
56
+ changedFiles++;
57
+ }
58
+ }
59
+
60
+ console.log(`Rewrote ${changedImports} import(s) across ${changedFiles} file(s).`);
@@ -0,0 +1,198 @@
1
+ import { Component, Input, type OnInit } from '@angular/core';
2
+ import { type Meta, moduleMetadata, type StoryObj } from '@storybook/angular';
3
+ import {
4
+ getTokenGroups,
5
+ getTokenValue,
6
+ getTypographyScale,
7
+ type TokenGroupWithTokens,
8
+ } from '@surfnet/curve-storybook-config';
9
+
10
+ /**
11
+ * Renders the named token groups. The selected `theme` / `mode` come in as inputs
12
+ * (read from the toolbar globals in the story), and values are looked up straight
13
+ * from the `@surfnet/curve-tokens` map. Swatch visuals still use `var(--token)` so they
14
+ * reflect the live painted theme.
15
+ */
16
+ @Component({
17
+ selector: 'surf-design-tokens',
18
+ standalone: true,
19
+ template: `
20
+ <div class="max-w-5xl text-foreground">
21
+ @for (entry of groups; track entry.group.id) {
22
+ <section class="mb-12">
23
+ <h3 class="text-lg font-semibold text-foreground">{{ entry.group.title }}</h3>
24
+ <p class="mb-5 max-w-prose text-sm text-muted-foreground">
25
+ {{ entry.group.description }}
26
+ </p>
27
+
28
+ @switch (entry.group.kind) {
29
+ @case ('color') {
30
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
31
+ @for (t of entry.tokens; track t.name) {
32
+ <div class="flex items-center gap-3">
33
+ <div
34
+ class="size-10 shrink-0 overflow-hidden rounded-md border border-border"
35
+ [style.background-image]="checker.image"
36
+ [style.background-size]="checker.size"
37
+ >
38
+ <div class="size-full" [style.background-color]="cssVar(t.name)"></div>
39
+ </div>
40
+ <div class="min-w-0">
41
+ <div class="truncate font-mono text-sm">{{ t.name }}</div>
42
+ <div class="truncate font-mono text-xs text-muted-foreground">
43
+ {{ val(t.name) || '—' }}
44
+ </div>
45
+ </div>
46
+ </div>
47
+ }
48
+ </div>
49
+ }
50
+ @case ('radius') {
51
+ <div class="flex flex-wrap gap-6">
52
+ @for (t of entry.tokens; track t.name) {
53
+ <div class="flex flex-col items-center gap-2 text-center">
54
+ <div
55
+ class="size-16 border-2 border-primary bg-muted"
56
+ [style.border-radius]="cssVar(t.name)"
57
+ ></div>
58
+ <div class="font-mono text-xs">{{ t.name }}</div>
59
+ <div class="font-mono text-xs text-muted-foreground">{{ val(t.name) }}</div>
60
+ </div>
61
+ }
62
+ </div>
63
+ }
64
+ @case ('font-family') {
65
+ @for (t of entry.tokens; track t.name) {
66
+ <div class="border-b border-border py-3 last:border-b-0">
67
+ <div class="flex items-baseline justify-between gap-6">
68
+ <span class="font-mono text-sm">{{ t.name }}</span>
69
+ <span class="shrink-0 font-mono text-xs text-muted-foreground">{{
70
+ val(t.name)
71
+ }}</span>
72
+ </div>
73
+ <p class="mt-1 text-xl" [style.font-family]="cssVar(t.name)">
74
+ The quick brown fox jumps over the lazy dog
75
+ </p>
76
+ </div>
77
+ }
78
+ }
79
+ @case ('font-weight') {
80
+ @for (t of entry.tokens; track t.name) {
81
+ <div
82
+ class="flex items-baseline justify-between gap-6 border-b border-border py-2 last:border-b-0"
83
+ >
84
+ <span class="text-lg" [style.font-weight]="val(t.name)">{{
85
+ weightLabel(t.name)
86
+ }}</span>
87
+ <span class="shrink-0 font-mono text-xs text-muted-foreground">{{
88
+ val(t.name)
89
+ }}</span>
90
+ </div>
91
+ }
92
+ }
93
+ @case ('font-size') {
94
+ @for (s of typeScale; track s.id) {
95
+ <div
96
+ class="flex items-baseline justify-between gap-6 border-b border-border py-3 last:border-b-0"
97
+ >
98
+ <span
99
+ class="truncate"
100
+ [style.font-size]="cssVar(s.fontSizeName)"
101
+ [style.line-height]="cssVar(s.lineHeightName)"
102
+ >{{ s.id }} — Almost before we knew it</span
103
+ >
104
+ <span class="shrink-0 font-mono text-xs text-muted-foreground"
105
+ >{{ val(s.fontSizeName) }} / {{ val(s.lineHeightName) }}</span
106
+ >
107
+ </div>
108
+ }
109
+ }
110
+ @default {
111
+ @for (t of entry.tokens; track t.name) {
112
+ <div
113
+ class="flex items-center justify-between gap-6 border-b border-border py-1.5 last:border-b-0"
114
+ >
115
+ <span class="font-mono text-sm">{{ t.name }}</span>
116
+ <span class="shrink-0 font-mono text-xs text-muted-foreground">{{
117
+ val(t.name) || '—'
118
+ }}</span>
119
+ </div>
120
+ }
121
+ }
122
+ }
123
+ </section>
124
+ }
125
+ </div>
126
+ `,
127
+ })
128
+ export class DesignTokensComponent implements OnInit {
129
+ @Input({ required: true }) ids: string[] = [];
130
+ @Input() theme = 'default';
131
+ @Input() mode = 'light';
132
+
133
+ protected groups: TokenGroupWithTokens[] = [];
134
+ protected readonly typeScale = getTypographyScale();
135
+ protected readonly checker = {
136
+ image: 'repeating-conic-gradient(#cbd5e1 0% 25%, #ffffff 0% 50%)',
137
+ size: '12px 12px',
138
+ };
139
+
140
+ ngOnInit(): void {
141
+ const byId = new Map(getTokenGroups().map((g) => [g.group.id, g]));
142
+ this.groups = this.ids
143
+ .map((id) => byId.get(id))
144
+ .filter((g): g is TokenGroupWithTokens => Boolean(g));
145
+ }
146
+
147
+ protected val(name: string): string {
148
+ return getTokenValue(this.theme, this.mode, name);
149
+ }
150
+
151
+ protected cssVar(name: string): string {
152
+ return `var(--${name})`;
153
+ }
154
+
155
+ protected weightLabel(name: string): string {
156
+ return name.replace('font-weight-', '');
157
+ }
158
+ }
159
+
160
+ const meta: Meta<DesignTokensComponent> = {
161
+ title: 'Foundations/Design Tokens',
162
+ component: DesignTokensComponent,
163
+ decorators: [moduleMetadata({ imports: [DesignTokensComponent] })],
164
+ parameters: {
165
+ layout: 'padded',
166
+ docs: {
167
+ description: {
168
+ component:
169
+ 'The design tokens shipped by `@surfnet/curve-tokens`. Use the **Theme** and **Mode** ' +
170
+ 'toolbar selectors to see each token’s resolved value in any theme or in light / dark mode.',
171
+ },
172
+ },
173
+ },
174
+ tags: ['autodocs'],
175
+ };
176
+
177
+ export default meta;
178
+
179
+ type Story = StoryObj<DesignTokensComponent>;
180
+
181
+ const story = (ids: string[]): Story => ({
182
+ render: (_args, { globals }) => ({
183
+ props: { ids, theme: globals['theme'] ?? 'default', mode: globals['mode'] ?? 'light' },
184
+ template: `<surf-design-tokens [ids]="ids" [theme]="theme" [mode]="mode"></surf-design-tokens>`,
185
+ }),
186
+ });
187
+
188
+ /** Semantic, chart, sidebar, alpha and internal colour tokens. */
189
+ export const Colors: Story = story(['semantic', 'chart', 'sidebar', 'alpha', 'custom']);
190
+
191
+ /** The type scale, font families and font weights. */
192
+ export const Typography: Story = story(['typography', 'font-family', 'font-weight']);
193
+
194
+ /** Corner radii, container widths and breakpoints. */
195
+ export const RadiusAndSpacing: Story = story(['radius', 'container', 'breakpoint']);
196
+
197
+ /** Shadow primitives and blur radii. */
198
+ export const ShadowsAndEffects: Story = story(['shadow', 'blur']);
@@ -0,0 +1,22 @@
1
+ import { HlmAvatar } from './lib/hlm-avatar';
2
+ import { HlmAvatarBadge } from './lib/hlm-avatar-badge';
3
+ import { HlmAvatarFallback } from './lib/hlm-avatar-fallback';
4
+ import { HlmAvatarGroup } from './lib/hlm-avatar-group';
5
+ import { HlmAvatarGroupCount } from './lib/hlm-avatar-group-count';
6
+ import { HlmAvatarImage } from './lib/hlm-avatar-image';
7
+
8
+ export * from './lib/hlm-avatar';
9
+ export * from './lib/hlm-avatar-badge';
10
+ export * from './lib/hlm-avatar-fallback';
11
+ export * from './lib/hlm-avatar-group';
12
+ export * from './lib/hlm-avatar-group-count';
13
+ export * from './lib/hlm-avatar-image';
14
+
15
+ export const HlmAvatarImports = [
16
+ HlmAvatar,
17
+ HlmAvatarBadge,
18
+ HlmAvatarFallback,
19
+ HlmAvatarGroup,
20
+ HlmAvatarGroupCount,
21
+ HlmAvatarImage,
22
+ ] as const;
@@ -0,0 +1,19 @@
1
+ import { Directive } from '@angular/core';
2
+ import { classes } from '../../../utils/src';
3
+
4
+ @Directive({
5
+ selector: '[hlmAvatarBadge],hlm-avatar-badge',
6
+ host: {
7
+ 'data-slot': 'avatar-badge',
8
+ },
9
+ })
10
+ export class HlmAvatarBadge {
11
+ constructor() {
12
+ classes(() => [
13
+ 'bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none',
14
+ 'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>ng-icon]:hidden',
15
+ 'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>ng-icon]:text-[calc(var(--spacing)*2)]',
16
+ 'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>ng-icon]:text-[calc(var(--spacing)*2)]',
17
+ ]);
18
+ }
19
+ }
@@ -0,0 +1,20 @@
1
+ import { Directive } from '@angular/core';
2
+ import { BrnAvatarFallback } from '@spartan-ng/brain/avatar';
3
+ import { classes } from '../../../utils/src';
4
+
5
+ @Directive({
6
+ selector: '[hlmAvatarFallback]',
7
+ exportAs: 'avatarFallback',
8
+ hostDirectives: [BrnAvatarFallback],
9
+ host: {
10
+ 'data-slot': 'avatar-fallback',
11
+ },
12
+ })
13
+ export class HlmAvatarFallback {
14
+ constructor() {
15
+ classes(
16
+ () =>
17
+ 'bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs',
18
+ );
19
+ }
20
+ }
@@ -0,0 +1,17 @@
1
+ import { Directive } from '@angular/core';
2
+ import { classes } from '../../../utils/src';
3
+
4
+ @Directive({
5
+ selector: '[hlmAvatarGroupCount],hlm-avatar-group-count',
6
+ host: {
7
+ 'data-slot': 'avatar-group-count',
8
+ },
9
+ })
10
+ export class HlmAvatarGroupCount {
11
+ constructor() {
12
+ classes(
13
+ () =>
14
+ 'bg-muted text-muted-foreground ring-background relative flex size-8 shrink-0 items-center justify-center rounded-full text-sm ring-2 group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>ng-icon]:text-base group-has-data-[size=lg]/avatar-group:[&>ng-icon]:text-xl group-has-data-[size=sm]/avatar-group:[&>ng-icon]:text-xs',
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { Directive } from '@angular/core';
2
+ import { classes } from '../../../utils/src';
3
+
4
+ @Directive({
5
+ selector: '[hlmAvatarGroup],hlm-avatar-group',
6
+ host: {
7
+ 'data-slot': 'avatar-group',
8
+ },
9
+ })
10
+ export class HlmAvatarGroup {
11
+ constructor() {
12
+ classes(
13
+ () =>
14
+ '*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2',
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,19 @@
1
+ import { Directive, inject } from '@angular/core';
2
+ import { BrnAvatarImage } from '@spartan-ng/brain/avatar';
3
+ import { classes } from '../../../utils/src';
4
+
5
+ @Directive({
6
+ selector: 'img[hlmAvatarImage]',
7
+ exportAs: 'avatarImage',
8
+ hostDirectives: [BrnAvatarImage],
9
+ host: {
10
+ 'data-slot': 'avatar-image',
11
+ },
12
+ })
13
+ export class HlmAvatarImage {
14
+ public readonly canShow = inject(BrnAvatarImage).canShow;
15
+
16
+ constructor() {
17
+ classes(() => 'rounded-full aspect-square size-full object-cover');
18
+ }
19
+ }
@@ -0,0 +1,107 @@
1
+ import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular';
2
+ import { avatarContract } from '@surfnet/curve-contracts';
3
+ import { HlmAvatar, HlmAvatarImports } from '..';
4
+
5
+ const meta: Meta<HlmAvatar> = {
6
+ title: 'Components/Avatar',
7
+ component: HlmAvatar,
8
+ decorators: [
9
+ moduleMetadata({
10
+ imports: [HlmAvatarImports],
11
+ }),
12
+ ],
13
+ parameters: {
14
+ docs: {
15
+ description: {
16
+ component: avatarContract.docs.description,
17
+ },
18
+ },
19
+ },
20
+ argTypes: {
21
+ size: {
22
+ control: 'inline-radio',
23
+ options: avatarContract.props.sizes,
24
+ description: avatarContract.props.sizes
25
+ .map((size) => `\`${size}\` — ${avatarContract.docs.sizes[size]}`)
26
+ .join('\n\n'),
27
+ table: {
28
+ defaultValue: { summary: avatarContract.defaults.sizes },
29
+ },
30
+ },
31
+ },
32
+ args: {
33
+ size: avatarContract.defaults.sizes,
34
+ },
35
+ };
36
+
37
+ export default meta;
38
+ type Story = StoryObj<HlmAvatar>;
39
+
40
+ /** An avatar with an image and a fallback. */
41
+ export const Default: Story = {
42
+ render: (args) => ({
43
+ props: args,
44
+ template: `
45
+ <hlm-avatar ${argsToTemplate(args)}>
46
+ <img hlmAvatarImage src="https://i.pravatar.cc/80?img=12" alt="Ada Lovelace">
47
+ <span hlmAvatarFallback>AL</span>
48
+ </hlm-avatar>
49
+ `,
50
+ }),
51
+ };
52
+
53
+ /** When the image fails to load, the initials fallback shows. */
54
+ export const Fallback: Story = {
55
+ render: () => ({
56
+ template: `
57
+ <hlm-avatar>
58
+ <img hlmAvatarImage src="https://example.com/does-not-exist.jpg" alt="Grace Hopper">
59
+ <span hlmAvatarFallback>GH</span>
60
+ </hlm-avatar>
61
+ `,
62
+ }),
63
+ };
64
+
65
+ /** Every size declared in the contract. */
66
+ export const Sizes: Story = {
67
+ render: () => ({
68
+ template: `
69
+ <div style="display:flex; align-items:center; gap:12px;">
70
+ ${avatarContract.props.sizes
71
+ .map(
72
+ (size) => `
73
+ <hlm-avatar size="${size}" title="${avatarContract.docs.sizes[size]}">
74
+ <span hlmAvatarFallback>
75
+ ${size.slice(0, 2).toUpperCase()}
76
+ </span>
77
+ </hlm-avatar>
78
+ `,
79
+ )
80
+ .join('')}
81
+ </div>
82
+ `,
83
+ }),
84
+ };
85
+
86
+ /** Stacked avatars with an overflow count. */
87
+ export const Group: Story = {
88
+ render: () => ({
89
+ template: `
90
+ <hlm-avatar-group>
91
+ <hlm-avatar>
92
+ <span hlmAvatarFallback>AL</span>
93
+ </hlm-avatar>
94
+
95
+ <hlm-avatar>
96
+ <span hlmAvatarFallback>GH</span>
97
+ </hlm-avatar>
98
+
99
+ <hlm-avatar>
100
+ <span hlmAvatarFallback>KJ</span>
101
+ </hlm-avatar>
102
+
103
+ <hlm-avatar-group-count>+3</hlm-avatar-group-count>
104
+ </hlm-avatar-group>
105
+ `,
106
+ }),
107
+ };
@@ -0,0 +1,32 @@
1
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+ import { BrnAvatar } from '@spartan-ng/brain/avatar';
3
+ import { classes } from '../../../utils/src';
4
+ import type { AvatarSizeName } from '@surfnet/curve-contracts';
5
+
6
+ @Component({
7
+ selector: 'hlm-avatar',
8
+ changeDetection: ChangeDetectionStrategy.OnPush,
9
+ host: {
10
+ 'data-slot': 'avatar',
11
+ '[attr.data-size]': 'size()',
12
+ },
13
+ template: `
14
+ @if (_image()?.canShow()) {
15
+ <ng-content select="[hlmAvatarImage],[brnAvatarImage]" />
16
+ } @else {
17
+ <ng-content select="[hlmAvatarFallback],[brnAvatarFallback]" />
18
+ }
19
+ <ng-content />
20
+ `,
21
+ })
22
+ export class HlmAvatar extends BrnAvatar {
23
+ public readonly size = input<AvatarSizeName>('default');
24
+
25
+ constructor() {
26
+ super();
27
+ classes(
28
+ () =>
29
+ 'size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 group/avatar after:border-border relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten',
30
+ );
31
+ }
32
+ }
@@ -0,0 +1,30 @@
1
+ import { HlmBreadcrumb } from './lib/hlm-breadcrumb';
2
+ import { HlmBreadcrumbEllipsis } from './lib/hlm-breadcrumb-ellipsis';
3
+ import { HlmBreadcrumbItem } from './lib/hlm-breadcrumb-item';
4
+ import { HlmBreadcrumbLink } from './lib/hlm-breadcrumb-link';
5
+ import { HlmBreadcrumbList } from './lib/hlm-breadcrumb-list';
6
+ import { HlmBreadcrumbPage } from './lib/hlm-breadcrumb-page';
7
+ import { HlmBreadcrumbSeparator } from './lib/hlm-breadcrumb-separator';
8
+
9
+ export * from './lib/hlm-breadcrumb';
10
+ export * from './lib/hlm-breadcrumb-ellipsis';
11
+ export * from './lib/hlm-breadcrumb-item';
12
+ export * from './lib/hlm-breadcrumb-link';
13
+ export * from './lib/hlm-breadcrumb-list';
14
+ export * from './lib/hlm-breadcrumb-page';
15
+ export * from './lib/hlm-breadcrumb-separator';
16
+
17
+ export const HlmBreadcrumbImports = [
18
+ HlmBreadcrumb,
19
+ HlmBreadcrumbEllipsis,
20
+ HlmBreadcrumbSeparator,
21
+ HlmBreadcrumbItem,
22
+ HlmBreadcrumbLink,
23
+ HlmBreadcrumbPage,
24
+ HlmBreadcrumbList,
25
+ ] as const;
26
+
27
+ /**
28
+ * @deprecated Use `HlmBreadcrumbImports` instead.
29
+ */
30
+ export const HlmBreadCrumbImports = HlmBreadcrumbImports;