@dereekb/dbx-components-mcp 13.11.15 → 13.11.17

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.
@@ -866,6 +866,146 @@
866
866
  "date-cell-index-range"
867
867
  ]
868
868
  },
869
+ {
870
+ "slug": "date-cell-schedule-encoded-week-regex",
871
+ "name": "DATE_CELL_SCHEDULE_ENCODED_WEEK_REGEX",
872
+ "kind": "const",
873
+ "category": "date",
874
+ "module": "@dereekb/date",
875
+ "subpath": "date/date.cell.schedule",
876
+ "signature": "const DATE_CELL_SCHEDULE_ENCODED_WEEK_REGEX: RegExp",
877
+ "description": "Regex that loosely validates a {@link DateCellScheduleEncodedWeek}.\n\nMatches zero-to-nine digit characters (any of `0-9`). Does not enforce ascending order or uniqueness of\ndigits — use {@link isDateCellScheduleEncodedWeek} for a runtime check that mirrors the type, or\n{@link dateCellScheduleEncodedWeek} to build a guaranteed-valid encoding from {@link DateCellScheduleDayCode}\nvalues.",
878
+ "params": [],
879
+ "returns": "RegExp",
880
+ "tags": [
881
+ "date",
882
+ "schedule",
883
+ "encoded",
884
+ "week",
885
+ "regex",
886
+ "validate",
887
+ "days",
888
+ "cell",
889
+ "date_cell_schedule_encoded_week_regex"
890
+ ],
891
+ "relatedSlugs": [
892
+ "is-date-cell-schedule-encoded-week",
893
+ "date-cell-schedule-encoded-week"
894
+ ]
895
+ },
896
+ {
897
+ "slug": "is-date-cell-schedule-encoded-week",
898
+ "name": "isDateCellScheduleEncodedWeek",
899
+ "kind": "function",
900
+ "category": "date",
901
+ "module": "@dereekb/date",
902
+ "subpath": "date/date.cell.schedule",
903
+ "signature": "isDateCellScheduleEncodedWeek(input: string): input is DateCellScheduleEncodedWeek",
904
+ "description": "Type guard that returns true if the input matches the {@link DATE_CELL_SCHEDULE_ENCODED_WEEK_REGEX} encoded-week format.\n\nThe regex check is loose — it allows duplicate digits and any digit ordering. To produce a canonically simplified\nencoding, use {@link dateCellScheduleEncodedWeek}.",
905
+ "params": [
906
+ {
907
+ "name": "input",
908
+ "type": "string",
909
+ "description": "- String to validate against the encoded week regex.",
910
+ "optional": false
911
+ }
912
+ ],
913
+ "returns": "Whether the string matches the encoded week format.",
914
+ "tags": [
915
+ "date",
916
+ "schedule",
917
+ "encoded",
918
+ "week",
919
+ "type-guard",
920
+ "validate",
921
+ "check",
922
+ "days",
923
+ "is",
924
+ "cell",
925
+ "isdatecellscheduleencodedweek"
926
+ ],
927
+ "example": "isDateCellScheduleEncodedWeek('24'); // true (Mon + Wed)\nisDateCellScheduleEncodedWeek('8'); // true (WEEKDAY shorthand)\nisDateCellScheduleEncodedWeek(''); // true (no days)\nisDateCellScheduleEncodedWeek('abc'); // false",
928
+ "relatedSlugs": [
929
+ "date-cell-schedule-encoded-week-regex",
930
+ "is-empty-date-cell-schedule-encoded-week",
931
+ "date-cell-schedule-encoded-week"
932
+ ]
933
+ },
934
+ {
935
+ "slug": "is-empty-date-cell-schedule-encoded-week",
936
+ "name": "isEmptyDateCellScheduleEncodedWeek",
937
+ "kind": "function",
938
+ "category": "date",
939
+ "module": "@dereekb/date",
940
+ "subpath": "date/date.cell.schedule",
941
+ "signature": "isEmptyDateCellScheduleEncodedWeek(input: string): input is DateCellScheduleEncodedWeek",
942
+ "description": "Returns true if the input represents an empty (no days selected) {@link DateCellScheduleEncodedWeek}.\n\nBoth the empty string `''` and `'0'` are treated as empty since `0` corresponds to\n{@link DateCellScheduleDayCode.NONE}, the no-op day code.",
943
+ "params": [
944
+ {
945
+ "name": "input",
946
+ "type": "string",
947
+ "description": "- String to check for emptiness.",
948
+ "optional": false
949
+ }
950
+ ],
951
+ "returns": "Whether the encoded week represents no selected days.",
952
+ "tags": [
953
+ "date",
954
+ "schedule",
955
+ "encoded",
956
+ "week",
957
+ "empty",
958
+ "check",
959
+ "type-guard",
960
+ "days",
961
+ "is",
962
+ "cell",
963
+ "isemptydatecellscheduleencodedweek"
964
+ ],
965
+ "example": "isEmptyDateCellScheduleEncodedWeek(''); // true\nisEmptyDateCellScheduleEncodedWeek('0'); // true (NONE code)\nisEmptyDateCellScheduleEncodedWeek('8'); // false (WEEKDAY)\nisEmptyDateCellScheduleEncodedWeek('24'); // false (Mon + Wed)",
966
+ "relatedSlugs": [
967
+ "is-date-cell-schedule-encoded-week",
968
+ "date-cell-schedule-encoded-week"
969
+ ]
970
+ },
971
+ {
972
+ "slug": "date-cell-schedule-encoded-week",
973
+ "name": "dateCellScheduleEncodedWeek",
974
+ "kind": "function",
975
+ "category": "date",
976
+ "module": "@dereekb/date",
977
+ "subpath": "date/date.cell.schedule",
978
+ "signature": "dateCellScheduleEncodedWeek(codes: Iterable<DateCellScheduleDayCode>): DateCellScheduleEncodedWeek",
979
+ "description": "Creates a simplified {@link DateCellScheduleEncodedWeek} from an iterable of {@link DateCellScheduleDayCode} values.\n\nCodes are passed through {@link simplifyDateCellScheduleDayCodes} so redundant individual day codes are collapsed\ninto shorthand (e.g. Mon-Fri becomes WEEKDAY `8`, Sat+Sun becomes WEEKEND `9`) and digits are emitted in ascending order.",
980
+ "params": [
981
+ {
982
+ "name": "codes",
983
+ "type": "Iterable<DateCellScheduleDayCode>",
984
+ "description": "- Day codes to encode into the compact string representation.",
985
+ "optional": false
986
+ }
987
+ ],
988
+ "returns": "The encoded week string.",
989
+ "tags": [
990
+ "date",
991
+ "schedule",
992
+ "encoded",
993
+ "week",
994
+ "encode",
995
+ "days",
996
+ "simplify",
997
+ "weekday",
998
+ "weekend",
999
+ "cell",
1000
+ "datecellscheduleencodedweek"
1001
+ ],
1002
+ "example": "// Encode weekdays only — collapses into the WEEKDAY shorthand\ndateCellScheduleEncodedWeek([DateCellScheduleDayCode.MONDAY, DateCellScheduleDayCode.TUESDAY, DateCellScheduleDayCode.WEDNESDAY, DateCellScheduleDayCode.THURSDAY, DateCellScheduleDayCode.FRIDAY]);\n// Returns '8'\n\n// Encode specific days\ndateCellScheduleEncodedWeek([DateCellScheduleDayCode.MONDAY, DateCellScheduleDayCode.WEDNESDAY]);\n// Returns '24'\n\n// Encode the full week\ndateCellScheduleEncodedWeek([DateCellScheduleDayCode.WEEKDAY, DateCellScheduleDayCode.WEEKEND]);\n// Returns '89'",
1003
+ "relatedSlugs": [
1004
+ "is-date-cell-schedule-encoded-week",
1005
+ "simplify-date-cell-schedule-day-codes",
1006
+ "expand-date-cell-schedule-day-codes"
1007
+ ]
1008
+ },
869
1009
  {
870
1010
  "slug": "time-duration-data-is-empty",
871
1011
  "name": "timeDurationDataIsEmpty",
@@ -1547,6 +1547,50 @@
1547
1547
  "label-block"
1548
1548
  ]
1549
1549
  },
1550
+ {
1551
+ "slug": "link",
1552
+ "selector": ".dbx-link",
1553
+ "source": "@dereekb/dbx-web",
1554
+ "module": "@dereekb/dbx-web",
1555
+ "file": "src/lib/router/layout/anchor/_anchor.scss",
1556
+ "line": 30,
1557
+ "declarations": [
1558
+ {
1559
+ "property": "text-decoration",
1560
+ "value": "underline"
1561
+ },
1562
+ {
1563
+ "property": "cursor",
1564
+ "value": "pointer"
1565
+ }
1566
+ ],
1567
+ "role": "interaction",
1568
+ "intent": "clickable link affordance — underlined text with pointer cursor; pair with `[dbxTextColor]` or a text color utility for the link color. NOTE: `<dbx-anchor>` renders an inner `<a class=\"dbx-anchor-a\">` with `text-decoration: none`, so apply this class to the projected content (e.g. a `<span>` inside the anchor) rather than the `<dbx-anchor>` host.",
1569
+ "seeAlso": [
1570
+ "link-hover",
1571
+ "dbx-u"
1572
+ ]
1573
+ },
1574
+ {
1575
+ "slug": "link-hover",
1576
+ "selector": ".dbx-link-hover",
1577
+ "source": "@dereekb/dbx-web",
1578
+ "module": "@dereekb/dbx-web",
1579
+ "file": "src/lib/router/layout/anchor/_anchor.scss",
1580
+ "line": 39,
1581
+ "declarations": [
1582
+ {
1583
+ "property": "cursor",
1584
+ "value": "pointer"
1585
+ }
1586
+ ],
1587
+ "role": "interaction",
1588
+ "intent": "clickable link affordance with hover-only underline — pointer cursor always, underline only on hover; pair with `[dbxTextColor]` or a text color utility for the link color. NOTE: `<dbx-anchor>` renders an inner `<a class=\"dbx-anchor-a\">` with `text-decoration: none`, so apply this class to the projected content (e.g. a `<span>` inside the anchor) rather than the `<dbx-anchor>` host.",
1589
+ "seeAlso": [
1590
+ "link",
1591
+ "dbx-u"
1592
+ ]
1593
+ },
1550
1594
  {
1551
1595
  "slug": "list-card-items-list",
1552
1596
  "selector": ".dbx-list-card-items-list",
@@ -1928,6 +1928,170 @@
1928
1928
  "example": "<dbx-bar>\n <span>Title</span>\n <dbx-spacer></dbx-spacer>\n <button mat-button>Action</button>\n</dbx-bar>",
1929
1929
  "minimalExample": "<dbx-spacer></dbx-spacer>"
1930
1930
  },
1931
+ {
1932
+ "slug": "style-body",
1933
+ "category": "layout",
1934
+ "kind": "directive",
1935
+ "selector": "[dbxStyleBody]",
1936
+ "className": "DbxStyleBodyDirective",
1937
+ "module": "@dereekb/dbx-web",
1938
+ "description": "Applies the current application style class from {@link DbxStyleService} to the document `<body>` element.\n\nPlace this directive on a root-level element so that the body tag receives the active style class.",
1939
+ "inputs": [],
1940
+ "outputs": [],
1941
+ "relatedSlugs": [
1942
+ "style",
1943
+ "style-service",
1944
+ "set-style"
1945
+ ],
1946
+ "example": "<div dbxStyleBody></div>",
1947
+ "minimalExample": "<div dbxStyleBody></div>"
1948
+ },
1949
+ {
1950
+ "slug": "color",
1951
+ "category": "layout",
1952
+ "kind": "directive",
1953
+ "selector": "[dbxColor]",
1954
+ "className": "DbxColorDirective",
1955
+ "module": "@dereekb/dbx-web",
1956
+ "description": "Applies a themed background color to the host element.\n\nAccepts either a {@link DbxThemeColor} string (e.g. `'primary'`, `'success'`) or a {@link DbxColorConfig}\nobject carrying arbitrary CSS color values (any hex, `rgb()`, `hsl()`, `var(...)`, `color-mix(...)`, etc.).\n\nOptionally set {@link dbxColorTone} (0-100) to control background opacity for a tonal/muted appearance.\nWhen tonal mode is active, the `dbx-color-tonal` CSS class is added and a CSS rule overrides the text\ncolor to the vibrant color (via `--dbx-bg-color-current`).\n\nThe standalone `[dbxColorTone]` input wins over `config.tone` when both are set; the same precedence\napplies to tonal mode.",
1957
+ "inputs": [
1958
+ {
1959
+ "name": "dbxColor",
1960
+ "type": "Maybe<DbxColorInput>",
1961
+ "description": "",
1962
+ "required": false
1963
+ },
1964
+ {
1965
+ "name": "dbxColorTone",
1966
+ "type": "Maybe<DbxColorTone>",
1967
+ "description": "Background tone level (0-100). When set, the background becomes semi-transparent\nand text color switches to the vibrant theme color for a tonal appearance.",
1968
+ "required": false
1969
+ }
1970
+ ],
1971
+ "outputs": [],
1972
+ "relatedSlugs": [
1973
+ "text-color",
1974
+ "color-service"
1975
+ ],
1976
+ "example": "<div [dbxColor]=\"'primary'\">Themed background</div>\n<div [dbxColor]=\"'primary'\" [dbxColorTone]=\"18\">Tonal themed</div>\n<div [dbxColor]=\"{ color: '#ff0066', contrast: 'white' }\">Custom hex</div>\n<div [dbxColor]=\"{ color: 'var(--mat-sys-tertiary)', contrast: 'var(--mat-sys-on-tertiary)', tone: 18 }\">Custom tonal</div>",
1977
+ "minimalExample": "<div [dbxColor]=\"'primary'\"></div>"
1978
+ },
1979
+ {
1980
+ "slug": "color-service",
1981
+ "category": "layout",
1982
+ "kind": "service",
1983
+ "selector": "",
1984
+ "className": "DbxColorService",
1985
+ "module": "@dereekb/dbx-web",
1986
+ "description": "Service that registers named {@link DbxColorConfigTemplate} entries and expands\n{@link DbxColorConfig} values that reference a template by key.\n\nSeeded via `provideDbxStyleService({ dbxColorServiceConfig: { templates: [...] } })`,\nthen injected by {@link DbxColorDirective} and {@link DbxTextColorDirective} to resolve\ntemplate references at render time.",
1987
+ "inputs": [],
1988
+ "outputs": [],
1989
+ "relatedSlugs": [
1990
+ "color",
1991
+ "text-color",
1992
+ "style-service"
1993
+ ],
1994
+ "example": "const colorService = inject(DbxColorService);\ncolorService.register({ key: 'brand-positive', config: { color: '#1f9b59', contrast: 'white', tone: 18 } });\ncolorService.expandColorConfig({ template: 'brand-positive' });\n// -> { template: 'brand-positive', color: '#1f9b59', contrast: 'white', tone: 18 }"
1995
+ },
1996
+ {
1997
+ "slug": "style",
1998
+ "category": "layout",
1999
+ "kind": "directive",
2000
+ "selector": "dbx-style, [dbxStyle], .dbx-style",
2001
+ "className": "DbxStyleDirective",
2002
+ "module": "@dereekb/dbx-web",
2003
+ "description": "Applies the current application style class from {@link DbxStyleService} to the host element.\n\nUse this directive to propagate the active style class onto any element or component.",
2004
+ "inputs": [],
2005
+ "outputs": [],
2006
+ "relatedSlugs": [
2007
+ "style-service",
2008
+ "style-body",
2009
+ "set-style"
2010
+ ],
2011
+ "example": "<div dbxStyle>Styled content</div>",
2012
+ "minimalExample": "<div dbxStyle></div>"
2013
+ },
2014
+ {
2015
+ "slug": "style-service",
2016
+ "category": "layout",
2017
+ "kind": "service",
2018
+ "selector": "",
2019
+ "className": "DbxStyleService",
2020
+ "module": "@dereekb/dbx-web",
2021
+ "description": "Manages application-wide style classes and suffix modes (e.g., dark mode).\n\nConsumers can set a default style config, override it with an observable config,\nand toggle style suffixes to switch between style variants at runtime.\n\nProvided via `provideDbxStyleService()` from `style.providers`.",
2022
+ "inputs": [],
2023
+ "outputs": [],
2024
+ "relatedSlugs": [
2025
+ "style",
2026
+ "set-style",
2027
+ "style-body",
2028
+ "color-service"
2029
+ ],
2030
+ "example": "const styleService = inject(DbxStyleService);\nstyleService.toggleDarkSuffix(); // toggle dark mode on/off\nstyleService.setStyleClassSuffix('dark'); // force dark mode"
2031
+ },
2032
+ {
2033
+ "slug": "set-style",
2034
+ "category": "layout",
2035
+ "kind": "directive",
2036
+ "selector": "[dbxSetStyle]",
2037
+ "className": "DbxSetStyleDirective",
2038
+ "module": "@dereekb/dbx-web",
2039
+ "description": "Sets the active application style for child elements and/or the global {@link DbxStyleService}.\n\nAccepts a {@link DbxStyleName} and optional suffixes, then applies the computed style class\ndepending on the configured {@link DbxSetStyleMode}.",
2040
+ "inputs": [
2041
+ {
2042
+ "name": "setStyleMode",
2043
+ "type": "DbxSetStyleMode",
2044
+ "description": "The mode for the style.\n\n- global: The style will be set on the parent DbxStyleService.\n- self: The style will be applied to the host element.",
2045
+ "required": false,
2046
+ "default": "'self'"
2047
+ },
2048
+ {
2049
+ "name": "dbxSetStyle",
2050
+ "type": "DbxStyleName",
2051
+ "description": "The input DbxStyleName to style the host element with.",
2052
+ "required": false
2053
+ },
2054
+ {
2055
+ "name": "suffixes",
2056
+ "type": "Maybe<string>",
2057
+ "description": "The input suffixes to allow to be applied to the",
2058
+ "required": false,
2059
+ "default": "undefined"
2060
+ }
2061
+ ],
2062
+ "outputs": [],
2063
+ "relatedSlugs": [
2064
+ "style",
2065
+ "style-service",
2066
+ "style-body"
2067
+ ],
2068
+ "example": "<div [dbxSetStyle]=\"'my-app'\" [setStyleMode]=\"'both'\" [suffixes]=\"'dark'\">\n <!-- Children inherit 'my-app' or 'my-app-dark' styling -->\n</div>",
2069
+ "minimalExample": "<div [dbxSetStyle]=\"'my-app'\"></div>"
2070
+ },
2071
+ {
2072
+ "slug": "text-color",
2073
+ "category": "layout",
2074
+ "kind": "directive",
2075
+ "selector": "[dbxTextColor]",
2076
+ "className": "DbxTextColorDirective",
2077
+ "module": "@dereekb/dbx-web",
2078
+ "description": "Applies a themed text color to the host element.\n\nAccepts either a {@link DbxThemeColor} string (e.g. `'primary'`, `'warn'`) or a {@link DbxColorConfig}\nobject carrying an arbitrary CSS color value. Unlike {@link DbxColorDirective} which sets the background,\nthis directive only sets the foreground text color.",
2079
+ "inputs": [
2080
+ {
2081
+ "name": "dbxTextColor",
2082
+ "type": "Maybe<DbxColorInput>",
2083
+ "description": "",
2084
+ "required": false
2085
+ }
2086
+ ],
2087
+ "outputs": [],
2088
+ "relatedSlugs": [
2089
+ "color",
2090
+ "color-service"
2091
+ ],
2092
+ "example": "<mat-icon [dbxTextColor]=\"'warn'\">error</mat-icon>\n<span [dbxTextColor]=\"'primary'\">Themed text</span>\n<span [dbxTextColor]=\"{ color: '#0066ff' }\">Custom hex text</span>",
2093
+ "minimalExample": "<span [dbxTextColor]=\"'primary'\"></span>"
2094
+ },
1931
2095
  {
1932
2096
  "slug": "click-to-copy-text",
1933
2097
  "category": "text",
@@ -270,10 +270,12 @@
270
270
  "dbx-anchor",
271
271
  "dbx-icon-tile",
272
272
  "mat-form-field",
273
- "mat-snack-bar"
273
+ "mat-snack-bar",
274
+ "dbx-link",
275
+ "dbx-link-hover"
274
276
  ],
275
- "info": "<p>\n Composes a marketing-style support page out of Angular Material cards plus dbx-web's\n <code>&lt;dbx-button&gt;</code>\n ,\n <code>&lt;dbx-anchor&gt;</code>\n , and\n <code>&lt;dbx-icon-tile&gt;</code>\n : an outlined hero\n <code>&lt;mat-card&gt;</code>\n for the search/CTA row and a responsive grid of outlined topic\n <code>&lt;mat-card&gt;</code>\n boxes whose rows are individual\n <code>&lt;dbx-anchor [anchor]&gt;</code>\n click targets.\n</p>\n<p>\n The topic grid uses Angular Flex Layout's CSS-Grid directives —\n <code>gdColumns=\"repeat(auto-fit, minmax(320px, 1fr))\"</code>\n +\n <code>gdGap</code>\n — so the cards auto-collapse 3 → 2 → 1 columns as the viewport narrows, with no breakpoint media queries or wrapping\n <code>fxFlex</code>\n shells needed. The hero CTA + search row uses\n <code>fxLayout=\"row\"</code>\n with\n <code>fxLayout.lt-md=\"column\"</code>\n so it stacks below the tablet breakpoint. The CTA itself composes the\n <code>.dbx-grow-button</code>\n +\n <code>.dbx-button-big</code>\n utilities (full-width, taller mat-button) instead of hand-rolled\n <code>::ng-deep</code>\n width / height overrides.\n</p>\n<p>\n Topic-card avatars are\n <code>&lt;dbx-icon-tile [round]&gt;</code>\n with\n <code>[dbxColor]=\"'primary'\"</code>\n +\n <code>[dbxColorTone]=\"18\"</code>\n for the tonal swatch. Each card body is a plain\n <code>&lt;ul&gt;</code>\n /\n <code>&lt;li&gt;</code>\n so the browser's default disc marker supplies the bullet, and topic links use\n <code>[dbxTextColor]=\"'primary'\"</code>\n (the\n <code>&lt;a&gt;</code>\n inside\n <code>dbx-anchor</code>\n already inherits color and is text-decoration-none by default).\n</p>\n<p>\n Each topic row supplies a\n <code>ClickableAnchor</code>\n with an\n <code>onClick</code>\n callback to\n <code>dbx-anchor</code>\n ; the callback opens a\n <code>MatSnackBar</code>\n with a \"Dismiss\" action so every link demonstrably triggers a button-bearing popup. Swap the\n <code>onClick</code>\n for a\n <code>ref</code>\n /\n <code>url</code>\n to route or open externally instead.\n</p>",
276
- "snippet": "<div class=\"dbx-p4 doc-support-page\">\n <h1 class=\"dbx-text-headline-large dbx-mb1\">Lorem Ipsum Support</h1>\n <p class=\"dbx-text-body-large dbx-mb4\">Dolor sit amet — we're here to help you succeed!</p>\n\n <mat-card appearance=\"outlined\" class=\"dbx-mb4\">\n <mat-card-content>\n <h3 class=\"dbx-text-title-medium dbx-mb3\">Need help? We're here.</h3>\n <div fxLayout=\"row\" fxLayout.lt-md=\"column\" fxLayoutGap=\"var(--dbx-padding-3)\" fxLayoutAlign=\"start stretch\">\n <dbx-button class=\"dbx-grow-button dbx-button-form-field-height\" fxFlex=\"1 1 50%\" flat icon=\"help_outline\" text=\"Chat with Support\" customButtonColor=\"var(--mat-sys-inverse-surface)\" customTextColor=\"var(--mat-sys-inverse-on-surface)\" (buttonClick)=\"onChatClick()\"></dbx-button>\n <mat-form-field class=\"dbx-p0\" appearance=\"outline\" fxFlex=\"1 1 50%\" subscriptSizing=\"dynamic\">\n <input matInput placeholder=\"Search for a topic or question…\" />\n </mat-form-field>\n </div>\n </mat-card-content>\n </mat-card>\n\n <div gdColumns=\"repeat(auto-fit, minmax(320px, 1fr))\" gdGap=\"var(--dbx-padding-5)\">\n @for (group of topics; track group.title) {\n <mat-card appearance=\"outlined\">\n <mat-card-content>\n <div fxLayout=\"row\" fxLayoutAlign=\"start center\" fxLayoutGap=\"var(--dbx-padding-2)\" class=\"dbx-mb3\">\n <dbx-icon-tile class=\"doc-support-topic-avatar\" [icon]=\"group.icon\" [round]=\"true\"></dbx-icon-tile>\n <span class=\"dbx-text-title-medium\">{{ group.title }}</span>\n </div>\n <ul class=\"doc-support-topic-links\">\n @for (link of group.links; track link.label) {\n <li class=\"dbx-pb2\">\n <dbx-anchor [dbxTextColor]=\"'primary'\" [anchor]=\"anchorFor(link)\">{{ link.label }}</dbx-anchor>\n </li>\n }\n </ul>\n </mat-card-content>\n </mat-card>\n }\n </div>\n</div>",
277
+ "info": "<p>\n Composes a marketing-style support page out of Angular Material cards plus dbx-web's\n <code>&lt;dbx-button&gt;</code>\n ,\n <code>&lt;dbx-anchor&gt;</code>\n , and\n <code>&lt;dbx-icon-tile&gt;</code>\n : an outlined hero\n <code>&lt;mat-card&gt;</code>\n for the search/CTA row and a responsive grid of outlined topic\n <code>&lt;mat-card&gt;</code>\n boxes whose rows are individual\n <code>&lt;dbx-anchor [anchor]&gt;</code>\n click targets.\n</p>\n<p>\n The topic grid uses Angular Flex Layout's CSS-Grid directives —\n <code>gdColumns=\"repeat(auto-fit, minmax(320px, 1fr))\"</code>\n +\n <code>gdGap</code>\n — so the cards auto-collapse 3 → 2 → 1 columns as the viewport narrows, with no breakpoint media queries or wrapping\n <code>fxFlex</code>\n shells needed. The hero CTA + search row uses\n <code>fxLayout=\"row\"</code>\n with\n <code>fxLayout.lt-md=\"column\"</code>\n so it stacks below the tablet breakpoint. The CTA itself composes the\n <code>.dbx-grow-button</code>\n +\n <code>.dbx-button-big</code>\n utilities (full-width, taller mat-button) instead of hand-rolled\n <code>::ng-deep</code>\n width / height overrides.\n</p>\n<p>\n Topic-card avatars are\n <code>&lt;dbx-icon-tile [round]&gt;</code>\n with\n <code>[dbxColor]=\"'primary'\"</code>\n +\n <code>[dbxColorTone]=\"18\"</code>\n for the tonal swatch. Each card body is a plain\n <code>&lt;ul&gt;</code>\n /\n <code>&lt;li&gt;</code>\n so the browser's default disc marker supplies the bullet, and topic links use\n <code>[dbxTextColor]=\"'primary'\"</code>\n for the link color plus a\n <code>&lt;span class=\"dbx-link-hover\"&gt;</code>\n wrapper around the projected label for the hover-only-underline affordance. The\n <code>.dbx-link-hover</code>\n /\n <code>.dbx-link</code>\n utilities pair with\n <code>.dbx-u</code>\n —\n <code>.dbx-link</code>\n is always underlined,\n <code>.dbx-link-hover</code>\n underlines on hover, both keep a pointer cursor. The class lives on the projected\n <code>&lt;span&gt;</code>\n rather than the\n <code>&lt;dbx-anchor&gt;</code>\n host because\n <code>dbx-anchor</code>\n 's internal\n <code>&lt;a class=\"dbx-anchor-a\"&gt;</code>\n bakes in\n <code>text-decoration: none</code>\n and would strip a host-level\n <code>.dbx-link</code>\n .\n</p>\n<p>\n Each topic row supplies a\n <code>ClickableAnchor</code>\n with an\n <code>onClick</code>\n callback to\n <code>dbx-anchor</code>\n ; the callback opens a\n <code>MatSnackBar</code>\n with a \"Dismiss\" action so every link demonstrably triggers a button-bearing popup. Swap the\n <code>onClick</code>\n for a\n <code>ref</code>\n /\n <code>url</code>\n to route or open externally instead.\n</p>",
278
+ "snippet": "<div class=\"dbx-p4 doc-support-page\">\n <h1 class=\"dbx-text-headline-large dbx-mb1\">Lorem Ipsum Support</h1>\n <p class=\"dbx-text-body-large dbx-mb4\">Dolor sit amet — we're here to help you succeed!</p>\n\n <mat-card appearance=\"outlined\" class=\"dbx-mb4\">\n <mat-card-content>\n <h3 class=\"dbx-text-title-medium dbx-mb3\">Need help? We're here.</h3>\n <div fxLayout=\"row\" fxLayout.lt-md=\"column\" fxLayoutGap=\"var(--dbx-padding-3)\" fxLayoutAlign=\"start stretch\">\n <dbx-button class=\"dbx-grow-button dbx-button-form-field-height\" fxFlex=\"1 1 50%\" flat icon=\"help_outline\" text=\"Chat with Support\" customButtonColor=\"var(--mat-sys-inverse-surface)\" customTextColor=\"var(--mat-sys-inverse-on-surface)\" (buttonClick)=\"onChatClick()\"></dbx-button>\n <mat-form-field class=\"dbx-p0\" appearance=\"outline\" fxFlex=\"1 1 50%\" subscriptSizing=\"dynamic\">\n <input matInput placeholder=\"Search for a topic or question…\" />\n </mat-form-field>\n </div>\n </mat-card-content>\n </mat-card>\n\n <div gdColumns=\"repeat(auto-fit, minmax(320px, 1fr))\" gdGap=\"var(--dbx-padding-5)\">\n @for (group of topics; track group.title) {\n <mat-card appearance=\"outlined\">\n <mat-card-content>\n <div fxLayout=\"row\" fxLayoutAlign=\"start center\" fxLayoutGap=\"var(--dbx-padding-2)\" class=\"dbx-mb3\">\n <dbx-icon-tile class=\"doc-support-topic-avatar\" [icon]=\"group.icon\" [round]=\"true\"></dbx-icon-tile>\n <span class=\"dbx-text-title-medium\">{{ group.title }}</span>\n </div>\n <ul class=\"doc-support-topic-links\">\n @for (link of group.links; track link.label) {\n <li class=\"dbx-pb2\">\n <dbx-anchor [dbxTextColor]=\"'primary'\" [anchor]=\"anchorFor(link)\">\n <span class=\"dbx-link-hover\">{{ link.label }}</span>\n </dbx-anchor>\n </li>\n }\n </ul>\n </mat-card-content>\n </mat-card>\n }\n </div>\n</div>",
277
279
  "uses": []
278
280
  }
279
281
  ]
@@ -264,7 +264,7 @@
264
264
  "whatItFlags": "A composite index required by tagged factories is missing from the committed `firestore.indexes.json`.",
265
265
  "whenItApplies": "Every entry in the generated composite set that has no equal entry in the committed JSON.",
266
266
  "whenItDoesNotApply": "Factories tagged `@dbxModelFirebaseIndexManual` — their shapes are author-managed and excluded from drift.",
267
- "canonicalFix": "Run the `scan-model-firebase-indexes` / `generate-firestore-indexes` CLIs and commit the regenerated `firestore.indexes.json`."
267
+ "canonicalFix": "Run `dbx-cli-generate-firestore-indexes --component <dir>` (and the `scan-model-firebase-indexes` CLI if the manifest is also stale) and commit the regenerated `firestore.indexes.json`."
268
268
  },
269
269
  {
270
270
  "code": "MODEL_FIREBASE_INDEX_COMPOSITE_REMOVED",
@@ -647,6 +647,70 @@
647
647
  }
648
648
  ]
649
649
  },
650
+ {
651
+ "code": "MODEL_GROUP_MISSING_CRUD_SPEC",
652
+ "source": "dbx_model_test_validate_app",
653
+ "severity": "warning",
654
+ "title": "A model group declared on the component side has no `<group>.crud.spec.ts`",
655
+ "whatItFlags": "A model group declared on the component side has no `<group>.crud.spec.ts`\nfile on the API side. Every model group is expected to have at least a\nbaseline CRUD spec exercising create / read / update / delete plus\npermission paths. Scenario specs are optional.",
656
+ "whenItApplies": "Every model group enumerated from the `-firebase` component (`<componentDir>/src/lib/model/<group>/`) that doesn't have a matching `<group>.crud.spec.ts` under `<apiDir>/src/app/function/<group>/`.",
657
+ "whenItDoesNotApply": "Reserved folders (`system`, `notification`, `storagefile`) — those follow distinct test layouts. Cross-group integration tests likewise don't satisfy this rule for their host group.",
658
+ "canonicalFix": "Add `<apiDir>/src/app/function/<group>/<group>.crud.spec.ts` covering the CRUD function map for the group. Use `dbx_model_test_convention` to fetch the canonical path.",
659
+ "seeAlso": [
660
+ {
661
+ "kind": "tool",
662
+ "target": "dbx_model_test_convention"
663
+ }
664
+ ]
665
+ },
666
+ {
667
+ "code": "TEST_FILE_DRIFT_RENAME",
668
+ "source": "dbx_model_test_validate_app",
669
+ "severity": "warning",
670
+ "title": "Spec filename uses a `crud` / `scenario` segment that is not directly",
671
+ "whatItFlags": "Spec filename uses a `crud` / `scenario` segment that is not directly\nafter the group name (e.g. `worker.payroll.scenario.spec.ts` or\n`worker.pay.crud.spec.ts`). The canonical form places the bucket\nmarker immediately after the group: `<group>.scenario.<sub>.spec.ts`\nor `<group>.crud.<sub>.spec.ts`.",
672
+ "whenItApplies": "Any spec file under `<apiDir>/src/app/function/<group>/` where the `crud` / `scenario` segment is not the first non-group segment.",
673
+ "whenItDoesNotApply": "Canonical subgroup names like `job.crud.requirement.spec.ts` — there `crud` is already first.",
674
+ "canonicalFix": "Rename to the canonical `<group>.<bucket>.<sub>.spec.ts` form. The validator emits the recommended filename as part of the violation message.",
675
+ "seeAlso": [
676
+ {
677
+ "kind": "tool",
678
+ "target": "dbx_model_test_convention"
679
+ }
680
+ ]
681
+ },
682
+ {
683
+ "code": "TEST_FILE_MISSING_BUCKET",
684
+ "source": "dbx_model_test_validate_app",
685
+ "severity": "warning",
686
+ "title": "Spec filename is missing a `crud` / `scenario` bucket segment entirely",
687
+ "whatItFlags": "Spec filename is missing a `crud` / `scenario` bucket segment entirely\n(e.g. `worker.system.spec.ts`). The validator can't tell whether the\nfile documents CRUD behavior or a workflow scenario — the author must\nchoose one.",
688
+ "whenItApplies": "Spec files where no segment after the group name is `crud` or `scenario`.",
689
+ "whenItDoesNotApply": "Files that already have a `crud` / `scenario` marker anywhere in the name (those surface `TEST_FILE_DRIFT_RENAME` instead when misplaced).",
690
+ "canonicalFix": "Decide whether the file's tests are CRUD-flavored or scenario-flavored, then rename to `<group>.crud.<sub>.spec.ts` or `<group>.scenario.<sub>.spec.ts`. Default to `scenario` when in doubt.",
691
+ "seeAlso": [
692
+ {
693
+ "kind": "tool",
694
+ "target": "dbx_model_test_convention"
695
+ }
696
+ ]
697
+ },
698
+ {
699
+ "code": "TEST_FILE_NON_GROUP_PLACEMENT",
700
+ "source": "dbx_model_test_validate_app",
701
+ "severity": "warning",
702
+ "title": "Spec file's first filename segment doesn't match its parent folder",
703
+ "whatItFlags": "Spec file's first filename segment doesn't match its parent folder\n(e.g. `storagefile.scenario.jobrequirement.spec.ts` placed under the\n`job/` folder). Tests should live in the folder of the model group\nnamed by their filename prefix.",
704
+ "whenItApplies": "Any `.spec.ts` whose first dot-separated segment differs from the immediate parent folder name.",
705
+ "whenItDoesNotApply": "Spec files that intentionally test cross-model integration — move those to the primary group's folder and rename so the prefix matches, or keep them as scenario sub-buckets of one side.",
706
+ "canonicalFix": "Move the file into `<apiDir>/src/app/function/<first-segment>/` so the prefix matches the folder, or rename the file's first segment to match the current folder.",
707
+ "seeAlso": [
708
+ {
709
+ "kind": "tool",
710
+ "target": "dbx_model_test_convention"
711
+ }
712
+ ]
713
+ },
650
714
  {
651
715
  "code": "FILE_GROUP_INTERFACE_AFTER_MODEL",
652
716
  "source": "dbx_model_validate",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-components-mcp",
3
- "version": "13.11.15",
3
+ "version": "13.11.17",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dbx-components-mcp": "dbx-components-mcp.js"
@@ -15,19 +15,19 @@
15
15
  "@angular/core": "21.2.11",
16
16
  "@angular/forms": "21.2.11",
17
17
  "@angular/material": "^21.2.9",
18
- "@dereekb/dbx-cli": "13.11.15",
19
- "@dereekb/dbx-core": "13.11.15",
20
- "@dereekb/dbx-firebase": "13.11.15",
21
- "@dereekb/dbx-form": "13.11.15",
22
- "@dereekb/dbx-web": "13.11.15",
23
- "@dereekb/firebase": "13.11.15",
24
- "@dereekb/firebase-server": "13.11.15",
25
- "@dereekb/rxjs": "13.11.15",
26
- "@dereekb/util": "13.11.15",
18
+ "@dereekb/dbx-cli": "13.11.17",
19
+ "@dereekb/dbx-core": "13.11.17",
20
+ "@dereekb/dbx-firebase": "13.11.17",
21
+ "@dereekb/dbx-form": "13.11.17",
22
+ "@dereekb/dbx-web": "13.11.17",
23
+ "@dereekb/firebase": "13.11.17",
24
+ "@dereekb/firebase-server": "13.11.17",
25
+ "@dereekb/rxjs": "13.11.17",
26
+ "@dereekb/util": "13.11.17",
27
27
  "@uirouter/angular": "21.0.0",
28
28
  "rxjs": "^7.8.2"
29
29
  },
30
30
  "devDependencies": {
31
- "@dereekb/model": "13.11.15"
31
+ "@dereekb/model": "13.11.17"
32
32
  }
33
33
  }