@sankhyatronics/sankhya-cms 0.0.0-stage → 0.0.9

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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -3
  3. package/custom-elements.json +4008 -0
  4. package/dist/bento-grid/index.d.ts +27 -0
  5. package/dist/bento-grid/index.js +10 -0
  6. package/dist/bento-grid/index.js.map +1 -0
  7. package/dist/card/index.d.ts +20 -0
  8. package/dist/card/index.js +9 -0
  9. package/dist/card/index.js.map +1 -0
  10. package/dist/carousel/index.d.ts +44 -0
  11. package/dist/carousel/index.js +10 -0
  12. package/dist/carousel/index.js.map +1 -0
  13. package/dist/chunk-23IHTBAB.js +557 -0
  14. package/dist/chunk-23IHTBAB.js.map +1 -0
  15. package/dist/chunk-2VZFHTBC.js +203 -0
  16. package/dist/chunk-2VZFHTBC.js.map +1 -0
  17. package/dist/chunk-3ERKQNZT.js +155 -0
  18. package/dist/chunk-3ERKQNZT.js.map +1 -0
  19. package/dist/chunk-3JU3R22O.js +193 -0
  20. package/dist/chunk-3JU3R22O.js.map +1 -0
  21. package/dist/chunk-3TCP25JG.js +347 -0
  22. package/dist/chunk-3TCP25JG.js.map +1 -0
  23. package/dist/chunk-3VE6VFJ4.js +87 -0
  24. package/dist/chunk-3VE6VFJ4.js.map +1 -0
  25. package/dist/chunk-4HKSA5UT.js +312 -0
  26. package/dist/chunk-4HKSA5UT.js.map +1 -0
  27. package/dist/chunk-5REZSZCK.js +266 -0
  28. package/dist/chunk-5REZSZCK.js.map +1 -0
  29. package/dist/chunk-5UFRGZWG.js +268 -0
  30. package/dist/chunk-5UFRGZWG.js.map +1 -0
  31. package/dist/chunk-74MILER4.js +215 -0
  32. package/dist/chunk-74MILER4.js.map +1 -0
  33. package/dist/chunk-7PSYCTEN.js +118 -0
  34. package/dist/chunk-7PSYCTEN.js.map +1 -0
  35. package/dist/chunk-E52WO7R7.js +279 -0
  36. package/dist/chunk-E52WO7R7.js.map +1 -0
  37. package/dist/chunk-EUXUH3YW.js +15 -0
  38. package/dist/chunk-EUXUH3YW.js.map +1 -0
  39. package/dist/chunk-IMACP7HN.js +156 -0
  40. package/dist/chunk-IMACP7HN.js.map +1 -0
  41. package/dist/chunk-LHU23TF6.js +273 -0
  42. package/dist/chunk-LHU23TF6.js.map +1 -0
  43. package/dist/chunk-OEX5FDMR.js +250 -0
  44. package/dist/chunk-OEX5FDMR.js.map +1 -0
  45. package/dist/chunk-RHAOQ6K4.js +138 -0
  46. package/dist/chunk-RHAOQ6K4.js.map +1 -0
  47. package/dist/chunk-RMIJVTFS.js +397 -0
  48. package/dist/chunk-RMIJVTFS.js.map +1 -0
  49. package/dist/chunk-TIU73O7I.js +221 -0
  50. package/dist/chunk-TIU73O7I.js.map +1 -0
  51. package/dist/chunk-USNFT4DU.js +125 -0
  52. package/dist/chunk-USNFT4DU.js.map +1 -0
  53. package/dist/chunk-VYON56QO.js +90 -0
  54. package/dist/chunk-VYON56QO.js.map +1 -0
  55. package/dist/chunk-WCO7JAIK.js +195 -0
  56. package/dist/chunk-WCO7JAIK.js.map +1 -0
  57. package/dist/chunk-XHBC23TV.js +254 -0
  58. package/dist/chunk-XHBC23TV.js.map +1 -0
  59. package/dist/chunk-XXPBFHWR.js +70 -0
  60. package/dist/chunk-XXPBFHWR.js.map +1 -0
  61. package/dist/chunk-YYK4GQUW.js +299 -0
  62. package/dist/chunk-YYK4GQUW.js.map +1 -0
  63. package/dist/cms-theme.css +15 -0
  64. package/dist/content-block/index.d.ts +25 -0
  65. package/dist/content-block/index.js +11 -0
  66. package/dist/content-block/index.js.map +1 -0
  67. package/dist/cookie-consent/index.d.ts +33 -0
  68. package/dist/cookie-consent/index.js +9 -0
  69. package/dist/cookie-consent/index.js.map +1 -0
  70. package/dist/dropdown/index.d.ts +89 -0
  71. package/dist/dropdown/index.js +9 -0
  72. package/dist/dropdown/index.js.map +1 -0
  73. package/dist/feature-split/index.d.ts +38 -0
  74. package/dist/feature-split/index.js +11 -0
  75. package/dist/feature-split/index.js.map +1 -0
  76. package/dist/features-section/index.d.ts +50 -0
  77. package/dist/features-section/index.js +14 -0
  78. package/dist/features-section/index.js.map +1 -0
  79. package/dist/footer/index.d.ts +64 -0
  80. package/dist/footer/index.js +9 -0
  81. package/dist/footer/index.js.map +1 -0
  82. package/dist/header/index.d.ts +112 -0
  83. package/dist/header/index.js +10 -0
  84. package/dist/header/index.js.map +1 -0
  85. package/dist/hero/index.d.ts +32 -0
  86. package/dist/hero/index.js +10 -0
  87. package/dist/hero/index.js.map +1 -0
  88. package/dist/icon-button/index.d.ts +23 -0
  89. package/dist/icon-button/index.js +9 -0
  90. package/dist/icon-button/index.js.map +1 -0
  91. package/dist/index.d.ts +22 -0
  92. package/dist/index.js +93 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/items-accordion/index.d.ts +31 -0
  95. package/dist/items-accordion/index.js +10 -0
  96. package/dist/items-accordion/index.js.map +1 -0
  97. package/dist/markdown/index.d.ts +33 -0
  98. package/dist/markdown/index.js +9 -0
  99. package/dist/markdown/index.js.map +1 -0
  100. package/dist/menu-grid/index.d.ts +21 -0
  101. package/dist/menu-grid/index.js +9 -0
  102. package/dist/menu-grid/index.js.map +1 -0
  103. package/dist/menu-grid-item/index.d.ts +53 -0
  104. package/dist/menu-grid-item/index.js +9 -0
  105. package/dist/menu-grid-item/index.js.map +1 -0
  106. package/dist/menu-item/index.d.ts +50 -0
  107. package/dist/menu-item/index.js +9 -0
  108. package/dist/menu-item/index.js.map +1 -0
  109. package/dist/promo-banner/index.d.ts +22 -0
  110. package/dist/promo-banner/index.js +10 -0
  111. package/dist/promo-banner/index.js.map +1 -0
  112. package/dist/schema/index.d.ts +72 -0
  113. package/dist/schema/index.js +75 -0
  114. package/dist/schema/index.js.map +1 -0
  115. package/dist/select/index.d.ts +77 -0
  116. package/dist/select/index.js +10 -0
  117. package/dist/select/index.js.map +1 -0
  118. package/dist/shared/icons.d.ts +77 -0
  119. package/dist/shared/icons.js +64 -0
  120. package/dist/shared/icons.js.map +1 -0
  121. package/dist/shared/styles.d.ts +90 -0
  122. package/dist/shared/styles.js +48 -0
  123. package/dist/shared/styles.js.map +1 -0
  124. package/dist/stats/index.d.ts +17 -0
  125. package/dist/stats/index.js +9 -0
  126. package/dist/stats/index.js.map +1 -0
  127. package/dist/testimonials/index.d.ts +25 -0
  128. package/dist/testimonials/index.js +10 -0
  129. package/dist/testimonials/index.js.map +1 -0
  130. package/dist/theme.css +292 -0
  131. package/dist/themes.css +65 -0
  132. package/dist/theming/index.d.ts +28 -0
  133. package/dist/theming/index.js +53 -0
  134. package/dist/theming/index.js.map +1 -0
  135. package/package.json +160 -3
@@ -0,0 +1,138 @@
1
+ // src/shared/styles.ts
2
+ import { css } from "lit";
3
+ var truncateStyles = css`
4
+ overflow: hidden;
5
+ text-overflow: ellipsis;
6
+ white-space: nowrap;
7
+ `;
8
+ var buttonResetStyles = css`
9
+ border: 0;
10
+ background: transparent;
11
+ font: inherit;
12
+ cursor: pointer;
13
+ `;
14
+ var ctaButtonStyles = css`
15
+ border: 0;
16
+ border-radius: var(--st-button-radius);
17
+ background: var(--st-button-primary-bg);
18
+ color: var(--st-button-primary-color);
19
+ font-weight: var(--st-button-font-weight);
20
+ cursor: pointer;
21
+ `;
22
+ var ctaButtonDisabledStyles = css`
23
+ background: var(--st-color-subtle);
24
+ cursor: not-allowed;
25
+ `;
26
+ var ctaButtonFillStyles = css`
27
+ background: var(--st-button-primary-bg);
28
+ color: var(--st-button-primary-color);
29
+ `;
30
+ var dropdownPanelStyles = css`
31
+ position: absolute;
32
+ top: 38px;
33
+ right: 0;
34
+ background: var(--st-color-surface);
35
+ border: 1px solid var(--st-color-border);
36
+ border-radius: var(--st-radius-sm);
37
+ box-shadow: 0 4px 16px var(--st-shadow-color-sm);
38
+ z-index: 30;
39
+ overflow: hidden;
40
+ `;
41
+ var emptyStateStyles = css`
42
+ text-align: center;
43
+ color: var(--st-color-text-muted);
44
+ font-size: var(--st-text-base);
45
+ `;
46
+ var tableShellStyles = css`
47
+ background: var(--st-color-surface);
48
+ border: 1px solid var(--st-color-border);
49
+ border-radius: var(--st-radius-md);
50
+ overflow: hidden;
51
+ `;
52
+ var resultRowStyles = css`
53
+ display: flex;
54
+ align-items: center;
55
+ gap: var(--st-space-12);
56
+ padding: var(--st-space-12) var(--st-space-14);
57
+ border: 1px solid var(--st-color-border);
58
+ border-radius: var(--st-radius-sm);
59
+ `;
60
+ var resultNameStyles = css`
61
+ flex: 1;
62
+ min-width: 120px;
63
+ font-size: var(--st-text-base);
64
+ font-weight: var(--st-font-weight-medium);
65
+ ${truncateStyles}
66
+ `;
67
+ var resultLinkStyles = css`
68
+ font-size: var(--st-text-sm);
69
+ font-weight: var(--st-font-weight-semibold);
70
+ color: var(--st-color-accent-text);
71
+ `;
72
+ var eyebrowLabelStyles = css`
73
+ font-weight: var(--st-font-weight-bold);
74
+ color: var(--st-color-text-muted);
75
+ text-transform: uppercase;
76
+ letter-spacing: var(--st-tracking-wide);
77
+ `;
78
+ var rowDividerStyles = css`
79
+ border-top: 1px solid var(--st-color-divider);
80
+ `;
81
+ var cardShellStyles = css`
82
+ background: var(--st-color-surface);
83
+ border: 1px solid var(--st-color-border);
84
+ border-radius: var(--st-radius-md);
85
+ `;
86
+ var itemNameStyles = css`
87
+ font-size: var(--st-text-base);
88
+ font-weight: var(--st-font-weight-semibold);
89
+ color: var(--st-color-text-strong);
90
+ `;
91
+ var itemCaptionStyles = css`
92
+ font-size: var(--st-text-xs);
93
+ color: var(--st-color-text-muted);
94
+ `;
95
+ var controlBaseStyles = css`
96
+ :is(button, a, input, select, textarea, [tabindex]):focus-visible {
97
+ outline: var(--st-focus-ring);
98
+ outline-offset: var(--st-focus-ring-offset);
99
+ }
100
+
101
+ input[type='checkbox'],
102
+ input[type='radio'] {
103
+ accent-color: var(--st-checkbox-color);
104
+ }
105
+ `;
106
+ var fontFamilyStyles = css`
107
+ font-family: var(--st-font-family);
108
+ `;
109
+ var rootFontFamilyStyles = css`
110
+ font-family: var(--st-font-family);
111
+ `;
112
+ var BREAKPOINT_TABLET = 900;
113
+ var BREAKPOINT_MOBILE = 500;
114
+
115
+ export {
116
+ truncateStyles,
117
+ buttonResetStyles,
118
+ ctaButtonStyles,
119
+ ctaButtonDisabledStyles,
120
+ ctaButtonFillStyles,
121
+ dropdownPanelStyles,
122
+ emptyStateStyles,
123
+ tableShellStyles,
124
+ resultRowStyles,
125
+ resultNameStyles,
126
+ resultLinkStyles,
127
+ eyebrowLabelStyles,
128
+ rowDividerStyles,
129
+ cardShellStyles,
130
+ itemNameStyles,
131
+ itemCaptionStyles,
132
+ controlBaseStyles,
133
+ fontFamilyStyles,
134
+ rootFontFamilyStyles,
135
+ BREAKPOINT_TABLET,
136
+ BREAKPOINT_MOBILE
137
+ };
138
+ //# sourceMappingURL=chunk-RHAOQ6K4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/styles.ts"],"sourcesContent":["import { css } from 'lit';\r\n\r\n/**\r\n * Reusable `css` fragments shared across component `static styles` blocks.\r\n *\r\n * Lit's `css` tag allows interpolating another `CSSResult` anywhere inside a\r\n * template, including mid-rule — `${fragment}` below expands to raw CSS\r\n * text, so these compose into a component's own selectors without forcing a\r\n * shared class name or touching `render()`. Each component keeps its own\r\n * selectors; only the duplicated property lists move here. Layout details\r\n * that genuinely vary per component (padding, gap, font-size, ...) stay\r\n * local, declared after a fragment so the cascade lets them override it.\r\n *\r\n * Add a fragment here only once a pattern is byte-identical (or near enough\r\n * that the deltas are better expressed as local overrides) across 3+\r\n * components — see `packages/suite-components/README.md#shared-style-fragments`.\r\n */\r\n\r\n/** Truncates overflowing text to a single line with an ellipsis. */\r\nexport const truncateStyles = css`\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n`;\r\n\r\n/**\r\n * Base for an unstyled, click-through interactive element (icon buttons,\r\n * menu triggers, link-like buttons) before component-specific padding,\r\n * font-size, and color are layered on.\r\n */\r\nexport const buttonResetStyles = css`\r\n border: 0;\r\n background: transparent;\r\n font: inherit;\r\n cursor: pointer;\r\n`;\r\n\r\n/**\r\n * The dark-filled primary-action button look (wizard Next/Generate/Run/Save,\r\n * active toggle buttons, ...). Pair with `ctaButtonDisabledStyles` on the\r\n * same selector's `:disabled` state.\r\n */\r\nexport const ctaButtonStyles = css`\r\n border: 0;\r\n border-radius: var(--st-button-radius);\r\n background: var(--st-button-primary-bg);\r\n color: var(--st-button-primary-color);\r\n font-weight: var(--st-button-font-weight);\r\n cursor: pointer;\r\n`;\r\n\r\nexport const ctaButtonDisabledStyles = css`\r\n background: var(--st-color-subtle);\r\n cursor: not-allowed;\r\n`;\r\n\r\n/**\r\n * Just the fill (background + color) from `ctaButtonStyles`, for selectors\r\n * that only need to swap an already-bordered/sized button into the CTA look\r\n * on an active/pressed/selected state (`[aria-pressed='true']`, `.active`,\r\n * ...) rather than defining the button from scratch.\r\n */\r\nexport const ctaButtonFillStyles = css`\r\n background: var(--st-button-primary-bg);\r\n color: var(--st-button-primary-color);\r\n`;\r\n\r\n/** Floating menu/dropdown panel chrome (user menu, language menu, ...). */\r\nexport const dropdownPanelStyles = css`\r\n position: absolute;\r\n top: 38px;\r\n right: 0;\r\n background: var(--st-color-surface);\r\n border: 1px solid var(--st-color-border);\r\n border-radius: var(--st-radius-sm);\r\n box-shadow: 0 4px 16px var(--st-shadow-color-sm);\r\n z-index: 30;\r\n overflow: hidden;\r\n`;\r\n\r\n/** \"No results\" / \"Nothing here.\" empty-state text. Padding stays local. */\r\nexport const emptyStateStyles = css`\r\n text-align: center;\r\n color: var(--st-color-text-muted);\r\n font-size: var(--st-text-base);\r\n`;\r\n\r\n/** Bordered, rounded shell wrapping a data table (browse/list views). */\r\nexport const tableShellStyles = css`\r\n background: var(--st-color-surface);\r\n border: 1px solid var(--st-color-border);\r\n border-radius: var(--st-radius-md);\r\n overflow: hidden;\r\n`;\r\n\r\n/** A single bordered result row with a name (truncated) + trailing link. */\r\nexport const resultRowStyles = css`\r\n display: flex;\r\n align-items: center;\r\n gap: var(--st-space-12);\r\n padding: var(--st-space-12) var(--st-space-14);\r\n border: 1px solid var(--st-color-border);\r\n border-radius: var(--st-radius-sm);\r\n`;\r\n\r\nexport const resultNameStyles = css`\r\n flex: 1;\r\n min-width: 120px;\r\n font-size: var(--st-text-base);\r\n font-weight: var(--st-font-weight-medium);\r\n ${truncateStyles}\r\n`;\r\n\r\nexport const resultLinkStyles = css`\r\n font-size: var(--st-text-sm);\r\n font-weight: var(--st-font-weight-semibold);\r\n color: var(--st-color-accent-text);\r\n`;\r\n\r\n/**\r\n * Uppercase, letter-spaced section/field \"eyebrow\" label (card headings,\r\n * table headers, field labels, ...). Font-size stays local since it varies\r\n * (11–12px) by context.\r\n */\r\nexport const eyebrowLabelStyles = css`\r\n font-weight: var(--st-font-weight-bold);\r\n color: var(--st-color-text-muted);\r\n text-transform: uppercase;\r\n letter-spacing: var(--st-tracking-wide);\r\n`;\r\n\r\n/** Top border separating stacked rows in a list/table (folder rows, data rows, table rows, ...). */\r\nexport const rowDividerStyles = css`\r\n border-top: 1px solid var(--st-color-divider);\r\n`;\r\n\r\n/**\r\n * Bordered, rounded surface shell for a card/tile/panel (as opposed to\r\n * `tableShellStyles`, which additionally clips overflow for tabular content).\r\n * Padding, gap, and cursor stay local since they vary by component.\r\n */\r\nexport const cardShellStyles = css`\r\n background: var(--st-color-surface);\r\n border: 1px solid var(--st-color-border);\r\n border-radius: var(--st-radius-md);\r\n`;\r\n\r\n/** A card/tile/row's bold primary name line (folder tiles/rows, template cards, ...). */\r\nexport const itemNameStyles = css`\r\n font-size: var(--st-text-base);\r\n font-weight: var(--st-font-weight-semibold);\r\n color: var(--st-color-text-strong);\r\n`;\r\n\r\n/** A card/tile/row's muted secondary caption line, paired with `itemNameStyles`. */\r\nexport const itemCaptionStyles = css`\r\n font-size: var(--st-text-xs);\r\n color: var(--st-color-text-muted);\r\n`;\r\n\r\n/**\r\n * Whole rules (not a property list) every component's stylesheet starts\r\n * with: routes keyboard focus rings and native checkbox/radio tint through\r\n * `--st-focus-ring` / `--st-checkbox-color`, whose defaults (`auto`) keep\r\n * the browser's own look until a host app themes them.\r\n */\r\nexport const controlBaseStyles = css`\r\n :is(button, a, input, select, textarea, [tabindex]):focus-visible {\r\n outline: var(--st-focus-ring);\r\n outline-offset: var(--st-focus-ring-offset);\r\n }\r\n\r\n input[type='checkbox'],\r\n input[type='radio'] {\r\n accent-color: var(--st-checkbox-color);\r\n }\r\n`;\r\n\r\n/** Default font stack, used by nearly every component. */\r\nexport const fontFamilyStyles = css`\r\n font-family: var(--st-font-family);\r\n`;\r\n\r\n/**\r\n * Font stack for page-root/chrome components (`st-template-hub`, the admin\r\n * and configuration pages, `st-app-header`, ...). Identical to\r\n * `fontFamilyStyles` now that `--st-font-family` always resolves via\r\n * `theme.css` (no per-usage fallback) — kept as a separate export so those\r\n * call sites stay semantically distinct if the two ever need to diverge\r\n * again.\r\n */\r\nexport const rootFontFamilyStyles = css`\r\n font-family: var(--st-font-family);\r\n`;\r\n\r\n/** Canonical responsive breakpoints (px). Interpolate as `${BREAKPOINT_TABLET}px`. */\r\nexport const BREAKPOINT_TABLET = 900;\r\nexport const BREAKPOINT_MOBILE = 500;\r\n"],"mappings":";AAAA,SAAS,WAAW;AAmBb,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAWvB,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAY1B,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASxB,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAWhC,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAM5B,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa5B,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAOzB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAQzB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASxB,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,IAK5B,cAAc;AAAA;AAGX,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAWzB,IAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ3B,IAAM,mBAAmB;AAAA;AAAA;AASzB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAOxB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAOvB,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAW1B,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa1B,IAAM,mBAAmB;AAAA;AAAA;AAYzB,IAAM,uBAAuB;AAAA;AAAA;AAK7B,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;","names":[]}
@@ -0,0 +1,397 @@
1
+ import {
2
+ buttonResetStyles,
3
+ controlBaseStyles,
4
+ fontFamilyStyles
5
+ } from "./chunk-RHAOQ6K4.js";
6
+ import {
7
+ __decorateClass
8
+ } from "./chunk-EUXUH3YW.js";
9
+
10
+ // src/dropdown/Dropdown.ts
11
+ import { LitElement, css, html, nothing } from "lit";
12
+ import { customElement, property, state } from "lit/decorators.js";
13
+ var Dropdown = class extends LitElement {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.label = "";
17
+ this.href = "";
18
+ this.iconPosition = "left";
19
+ this.iconSize = "1.2rem";
20
+ this.iconColor = "currentColor";
21
+ this.align = "left";
22
+ this.target = "_self";
23
+ this.triggerClassName = "";
24
+ this.itemGroupClassName = "";
25
+ this.itemClassName = "";
26
+ this.disabled = false;
27
+ this.active = false;
28
+ this.inline = false;
29
+ this.open = false;
30
+ this.hasContent = false;
31
+ this.hasIcon = false;
32
+ this.closeOnOutsideMouseDown = (event) => {
33
+ if (this.open && !event.composedPath().includes(this)) this.setOpen(false);
34
+ };
35
+ this.closeOnEscape = (event) => {
36
+ if (!this.open || event.key !== "Escape") return;
37
+ const focusWasInside = event.composedPath().includes(this);
38
+ this.setOpen(false);
39
+ if (focusWasInside) this.trigger()?.focus();
40
+ };
41
+ /** Enter/Space/ArrowDown open the panel and focus its first entry; ArrowUp its last. */
42
+ this.onTitleKeyDown = (event) => {
43
+ if (!this.hasContent || this.disabled) return;
44
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
45
+ event.preventDefault();
46
+ event.stopPropagation();
47
+ this.setOpen(true);
48
+ this.focusItem((items) => items[0]);
49
+ } else if (event.key === "ArrowUp") {
50
+ event.preventDefault();
51
+ event.stopPropagation();
52
+ this.setOpen(true);
53
+ this.focusItem((items) => items[items.length - 1]);
54
+ }
55
+ };
56
+ /** Arrow keys/Home/End move between the panel's entries; Tab closes the panel and moves on. */
57
+ this.onPanelKeyDown = (event) => {
58
+ if (event.key === "Tab") {
59
+ this.setOpen(false);
60
+ return;
61
+ }
62
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
63
+ const items = this.panelItems();
64
+ if (items.length === 0) return;
65
+ event.preventDefault();
66
+ event.stopPropagation();
67
+ const path = event.composedPath();
68
+ const current = items.findIndex((item) => path.includes(item));
69
+ const last = items.length - 1;
70
+ const next = event.key === "Home" ? 0 : event.key === "End" ? last : event.key === "ArrowDown" ? current < 0 || current === last ? 0 : current + 1 : current <= 0 ? last : current - 1;
71
+ items[next].focus();
72
+ };
73
+ this.onTitleClick = (event) => {
74
+ event.stopPropagation();
75
+ if (!this.hasContent) return;
76
+ event.preventDefault();
77
+ this.setOpen(!this.open);
78
+ };
79
+ /** A click anywhere inside the panel closes it, as in the source. */
80
+ this.onPanelClick = () => {
81
+ this.setOpen(false);
82
+ };
83
+ this.onContentSlotChange = (event) => {
84
+ this.hasContent = event.target.assignedNodes({ flatten: true }).some(
85
+ (node) => node.nodeType === Node.ELEMENT_NODE || !!node.textContent?.trim()
86
+ );
87
+ };
88
+ this.onIconSlotChange = (event) => {
89
+ this.hasIcon = event.target.assignedNodes({ flatten: true }).length > 0;
90
+ };
91
+ }
92
+ connectedCallback() {
93
+ super.connectedCallback();
94
+ document.addEventListener("mousedown", this.closeOnOutsideMouseDown);
95
+ window.addEventListener("keydown", this.closeOnEscape);
96
+ }
97
+ disconnectedCallback() {
98
+ document.removeEventListener("mousedown", this.closeOnOutsideMouseDown);
99
+ window.removeEventListener("keydown", this.closeOnEscape);
100
+ if (Dropdown.openDropdown === this) Dropdown.openDropdown = null;
101
+ super.disconnectedCallback();
102
+ }
103
+ updated(changed) {
104
+ if (!changed.has("open")) return;
105
+ if (this.open) {
106
+ const previous = Dropdown.openDropdown;
107
+ Dropdown.openDropdown = this;
108
+ if (previous && previous !== this) previous.setOpen(false);
109
+ } else if (Dropdown.openDropdown === this) {
110
+ Dropdown.openDropdown = null;
111
+ }
112
+ }
113
+ setOpen(open) {
114
+ if (this.open === open) return;
115
+ this.open = open;
116
+ this.dispatchEvent(new CustomEvent("dropdown-toggle", { detail: { open }, bubbles: true, composed: true }));
117
+ }
118
+ trigger() {
119
+ return this.renderRoot.querySelector(".menu-title");
120
+ }
121
+ /** Keyboard-reachable entries in the panel, in order: menu items (they delegate focus to their
122
+ * own link) plus any plain links/buttons/tabbable elements a caller slots in. */
123
+ panelItems() {
124
+ const slot = this.renderRoot.querySelector(".menu-dropdown slot");
125
+ if (!slot) return [];
126
+ const selector = 'st-menu-item, st-menu-grid-item, a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])';
127
+ const items = [];
128
+ for (const el of slot.assignedElements({ flatten: true })) {
129
+ if (el.matches(selector)) items.push(el);
130
+ items.push(...Array.from(el.querySelectorAll(selector)));
131
+ }
132
+ return items;
133
+ }
134
+ /** Moves focus into the panel once it has become visible (it is `visibility: hidden` until the
135
+ * `show` class lands, and hidden elements can't take focus). */
136
+ focusItem(pick) {
137
+ void this.updateComplete.then(() => pick(this.panelItems())?.focus());
138
+ }
139
+ render() {
140
+ const icon = html`<span
141
+ class="icon ${this.iconPosition === "left" ? "icon-left" : "icon-right"}"
142
+ ?hidden=${!this.hasIcon}
143
+ style="width:${this.iconSize};height:${this.iconSize};color:${this.iconColor}"
144
+ ><slot name="icon" @slotchange=${this.onIconSlotChange}></slot
145
+ ></span>`;
146
+ const titleContent = html`
147
+ ${this.iconPosition === "left" ? icon : nothing} ${this.label ? html`<span part="label">${this.label}</span>` : nothing}
148
+ ${this.iconPosition === "right" ? icon : nothing}
149
+ ${this.hasContent ? html`<span class="arrow" part="caret" aria-hidden="true">▼</span>` : nothing}
150
+ `;
151
+ const titleClass = `menu-title ${this.triggerClassName} ${this.open ? "active" : ""}`;
152
+ return html`
153
+ <div class="menu" part="menu">
154
+ ${this.href ? html`
155
+ <a
156
+ part="trigger"
157
+ class=${titleClass}
158
+ href=${this.href}
159
+ target=${this.target}
160
+ aria-haspopup=${this.hasContent ? "true" : "false"}
161
+ aria-expanded=${this.open}
162
+ aria-disabled=${this.disabled ? "true" : nothing}
163
+ @click=${this.onTitleClick}
164
+ @keydown=${this.onTitleKeyDown}
165
+ >
166
+ ${titleContent}
167
+ </a>
168
+ ` : html`
169
+ <button
170
+ part="trigger"
171
+ class=${titleClass}
172
+ type="button"
173
+ aria-haspopup="true"
174
+ aria-expanded=${this.open}
175
+ ?disabled=${this.disabled}
176
+ @click=${this.onTitleClick}
177
+ @keydown=${this.onTitleKeyDown}
178
+ >
179
+ ${titleContent}
180
+ </button>
181
+ `}
182
+ <div
183
+ part="panel"
184
+ class="menu-dropdown align-${this.align} ${this.itemGroupClassName} ${this.open ? "show" : ""}"
185
+ role="menu"
186
+ @click=${this.onPanelClick}
187
+ @keydown=${this.onPanelKeyDown}
188
+ >
189
+ <slot @slotchange=${this.onContentSlotChange}></slot>
190
+ </div>
191
+ </div>
192
+ `;
193
+ }
194
+ };
195
+ /** The open dropdown, if any — the source's `DropdownContext.activeDropdown`. */
196
+ Dropdown.openDropdown = null;
197
+ Dropdown.styles = css`
198
+ ${controlBaseStyles}
199
+
200
+ :host {
201
+ display: inline-block;
202
+ position: relative;
203
+ ${fontFamilyStyles}
204
+ }
205
+
206
+ .menu-title {
207
+ ${buttonResetStyles}
208
+ display: flex;
209
+ align-items: center;
210
+ justify-content: center;
211
+ box-sizing: border-box;
212
+ min-height: 40px;
213
+ padding: 12px;
214
+ gap: 8px;
215
+ text-align: center;
216
+ width: fit-content;
217
+ border-radius: var(--st-radius-sm);
218
+ color: var(--st-color-text);
219
+ font-size: 0.875rem;
220
+ text-decoration: none;
221
+ white-space: nowrap;
222
+ transition: background-color 0.3s ease;
223
+ }
224
+
225
+ .menu-title:hover,
226
+ .menu-title[aria-expanded='true'] {
227
+ background: var(--st-color-hover);
228
+ color: var(--st-color-text-strong);
229
+ }
230
+
231
+ :host([active]) .menu-title {
232
+ background: var(--st-color-accent-subtle);
233
+ color: var(--st-color-accent-text);
234
+ }
235
+
236
+ :host([disabled]) .menu-title {
237
+ color: var(--st-color-text-muted);
238
+ cursor: not-allowed;
239
+ pointer-events: none;
240
+ }
241
+
242
+ .icon {
243
+ display: inline-flex;
244
+ flex: none;
245
+ align-items: center;
246
+ justify-content: center;
247
+ }
248
+
249
+ .icon[hidden] {
250
+ display: none;
251
+ }
252
+
253
+ .arrow {
254
+ display: inline-block;
255
+ margin-left: 8px;
256
+ font-size: 0.75rem;
257
+ transition: transform 0.3s ease;
258
+ }
259
+
260
+ .menu-title[aria-expanded='true'] .arrow {
261
+ transform: rotate(180deg);
262
+ }
263
+
264
+ .menu-dropdown {
265
+ position: absolute;
266
+ top: 100%;
267
+ left: 0;
268
+ z-index: 100;
269
+ padding: 16px;
270
+ background: var(--st-color-surface);
271
+ color: var(--st-color-text);
272
+ border: 1px solid var(--st-color-border);
273
+ border-radius: var(--st-radius-sm);
274
+ box-shadow: 0 4px 16px var(--st-shadow-color-sm);
275
+ opacity: 0;
276
+ visibility: hidden;
277
+ transform: translateY(-10px);
278
+ /* visibility flips at once on open (so keyboard focus can move into the panel on the same
279
+ frame) and only after the 0.3s fade on close. */
280
+ transition:
281
+ opacity 0.3s ease,
282
+ transform 0.3s ease,
283
+ visibility 0s linear 0.3s;
284
+ pointer-events: none;
285
+ }
286
+
287
+ .menu-dropdown.align-right {
288
+ left: auto;
289
+ right: 0;
290
+ }
291
+
292
+ .menu-dropdown.show {
293
+ opacity: 1;
294
+ visibility: visible;
295
+ transform: translateY(0);
296
+ pointer-events: auto;
297
+ transition-delay: 0s;
298
+ }
299
+
300
+ /* Inline: the panel opens in the flow under its trigger and pushes what follows down, instead
301
+ of floating — the source's .touch-device layout. st-header turns this on for its menu while
302
+ the hamburger panel is showing. */
303
+ :host([inline]) {
304
+ display: block;
305
+ }
306
+
307
+ :host([inline]) .menu-title {
308
+ width: 100%;
309
+ justify-content: flex-start;
310
+ }
311
+
312
+ :host([inline]) .arrow {
313
+ margin-left: auto;
314
+ }
315
+
316
+ :host([inline]) .menu-dropdown {
317
+ display: none;
318
+ position: relative;
319
+ top: auto;
320
+ left: auto;
321
+ right: auto;
322
+ width: 100%;
323
+ box-sizing: border-box;
324
+ box-shadow: none;
325
+ opacity: 1;
326
+ visibility: visible;
327
+ transform: none;
328
+ }
329
+
330
+ :host([inline]) .menu-dropdown.show {
331
+ display: block;
332
+ pointer-events: auto;
333
+ }
334
+
335
+ @media (prefers-reduced-motion: reduce) {
336
+ .menu-dropdown,
337
+ .arrow {
338
+ transition: none;
339
+ }
340
+ }
341
+ `;
342
+ __decorateClass([
343
+ property({ type: String })
344
+ ], Dropdown.prototype, "label", 2);
345
+ __decorateClass([
346
+ property({ type: String })
347
+ ], Dropdown.prototype, "href", 2);
348
+ __decorateClass([
349
+ property({ type: String, attribute: "icon-position" })
350
+ ], Dropdown.prototype, "iconPosition", 2);
351
+ __decorateClass([
352
+ property({ type: String, attribute: "icon-size" })
353
+ ], Dropdown.prototype, "iconSize", 2);
354
+ __decorateClass([
355
+ property({ type: String, attribute: "icon-color" })
356
+ ], Dropdown.prototype, "iconColor", 2);
357
+ __decorateClass([
358
+ property({ type: String })
359
+ ], Dropdown.prototype, "align", 2);
360
+ __decorateClass([
361
+ property({ type: String })
362
+ ], Dropdown.prototype, "target", 2);
363
+ __decorateClass([
364
+ property({ type: String, attribute: "trigger-class-name" })
365
+ ], Dropdown.prototype, "triggerClassName", 2);
366
+ __decorateClass([
367
+ property({ type: String, attribute: "item-group-class-name" })
368
+ ], Dropdown.prototype, "itemGroupClassName", 2);
369
+ __decorateClass([
370
+ property({ type: String, attribute: "item-class-name" })
371
+ ], Dropdown.prototype, "itemClassName", 2);
372
+ __decorateClass([
373
+ property({ type: Boolean, reflect: true })
374
+ ], Dropdown.prototype, "disabled", 2);
375
+ __decorateClass([
376
+ property({ type: Boolean, reflect: true })
377
+ ], Dropdown.prototype, "active", 2);
378
+ __decorateClass([
379
+ property({ type: Boolean, reflect: true })
380
+ ], Dropdown.prototype, "inline", 2);
381
+ __decorateClass([
382
+ property({ type: Boolean, reflect: true })
383
+ ], Dropdown.prototype, "open", 2);
384
+ __decorateClass([
385
+ state()
386
+ ], Dropdown.prototype, "hasContent", 2);
387
+ __decorateClass([
388
+ state()
389
+ ], Dropdown.prototype, "hasIcon", 2);
390
+ Dropdown = __decorateClass([
391
+ customElement("st-dropdown")
392
+ ], Dropdown);
393
+
394
+ export {
395
+ Dropdown
396
+ };
397
+ //# sourceMappingURL=chunk-RMIJVTFS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dropdown/Dropdown.ts"],"sourcesContent":["import { LitElement, css, html, nothing } from 'lit';\r\nimport { customElement, property, state } from 'lit/decorators.js';\r\n\r\nimport { buttonResetStyles, controlBaseStyles, fontFamilyStyles } from '../shared/styles';\r\nimport type { DropdownToggleEventDetail } from './DropdownEvents';\r\n\r\n/**\r\n * A labeled trigger + slotted dropdown panel — nav menus (typically holding an `st-menu-grid`),\r\n * action menus, anything \"click a trigger, show content under it\". A 1:1 port of `SankhyaUI`'s\r\n * React `Dropdown` (`SankhyaCMS/SankhyaUI/src/components/Dropdown`): same markup\r\n * (`.menu` > `.menu-title` + `.menu-dropdown`), sizes, 0.3s slide/fade, and behavior —\r\n *\r\n * - only one dropdown is open at a time (the source's `DropdownProvider`, built in here, so no\r\n * provider element is needed);\r\n * - a mousedown outside, Escape, or a click inside the panel closes it;\r\n * - an `href` trigger toggles the panel when it has content, and is a plain link when it doesn't;\r\n * - `inline` opens the panel in the flow, the source's `.touch-device` layout.\r\n *\r\n * Colors and the panel's surface come from the suite theme (the source left those to the\r\n * consumer's `className`/`itemGroupClassName` utility classes). Two source rules are left out\r\n * because they break existing suite uses (`st-source-bar`'s site/library menus): the 100vw panel\r\n * at <=768px (overflows the screen from wherever the trigger sits; the header uses `inline`\r\n * instead) and `overflow: hidden` on slotted children (clips their focus rings). Suite additions\r\n * kept on top: `align`, `active`, `disabled`, `inline`, and the `dropdown-toggle` event.\r\n */\r\n@customElement('st-dropdown')\r\nexport class Dropdown extends LitElement {\r\n /** The open dropdown, if any — the source's `DropdownContext.activeDropdown`. */\r\n private static openDropdown: Dropdown | null = null;\r\n\r\n static styles = css`\r\n ${controlBaseStyles}\r\n\r\n :host {\r\n display: inline-block;\r\n position: relative;\r\n ${fontFamilyStyles}\r\n }\r\n\r\n .menu-title {\r\n ${buttonResetStyles}\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n box-sizing: border-box;\r\n min-height: 40px;\r\n padding: 12px;\r\n gap: 8px;\r\n text-align: center;\r\n width: fit-content;\r\n border-radius: var(--st-radius-sm);\r\n color: var(--st-color-text);\r\n font-size: 0.875rem;\r\n text-decoration: none;\r\n white-space: nowrap;\r\n transition: background-color 0.3s ease;\r\n }\r\n\r\n .menu-title:hover,\r\n .menu-title[aria-expanded='true'] {\r\n background: var(--st-color-hover);\r\n color: var(--st-color-text-strong);\r\n }\r\n\r\n :host([active]) .menu-title {\r\n background: var(--st-color-accent-subtle);\r\n color: var(--st-color-accent-text);\r\n }\r\n\r\n :host([disabled]) .menu-title {\r\n color: var(--st-color-text-muted);\r\n cursor: not-allowed;\r\n pointer-events: none;\r\n }\r\n\r\n .icon {\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .icon[hidden] {\r\n display: none;\r\n }\r\n\r\n .arrow {\r\n display: inline-block;\r\n margin-left: 8px;\r\n font-size: 0.75rem;\r\n transition: transform 0.3s ease;\r\n }\r\n\r\n .menu-title[aria-expanded='true'] .arrow {\r\n transform: rotate(180deg);\r\n }\r\n\r\n .menu-dropdown {\r\n position: absolute;\r\n top: 100%;\r\n left: 0;\r\n z-index: 100;\r\n padding: 16px;\r\n background: var(--st-color-surface);\r\n color: var(--st-color-text);\r\n border: 1px solid var(--st-color-border);\r\n border-radius: var(--st-radius-sm);\r\n box-shadow: 0 4px 16px var(--st-shadow-color-sm);\r\n opacity: 0;\r\n visibility: hidden;\r\n transform: translateY(-10px);\r\n /* visibility flips at once on open (so keyboard focus can move into the panel on the same\r\n frame) and only after the 0.3s fade on close. */\r\n transition:\r\n opacity 0.3s ease,\r\n transform 0.3s ease,\r\n visibility 0s linear 0.3s;\r\n pointer-events: none;\r\n }\r\n\r\n .menu-dropdown.align-right {\r\n left: auto;\r\n right: 0;\r\n }\r\n\r\n .menu-dropdown.show {\r\n opacity: 1;\r\n visibility: visible;\r\n transform: translateY(0);\r\n pointer-events: auto;\r\n transition-delay: 0s;\r\n }\r\n\r\n /* Inline: the panel opens in the flow under its trigger and pushes what follows down, instead\r\n of floating — the source's .touch-device layout. st-header turns this on for its menu while\r\n the hamburger panel is showing. */\r\n :host([inline]) {\r\n display: block;\r\n }\r\n\r\n :host([inline]) .menu-title {\r\n width: 100%;\r\n justify-content: flex-start;\r\n }\r\n\r\n :host([inline]) .arrow {\r\n margin-left: auto;\r\n }\r\n\r\n :host([inline]) .menu-dropdown {\r\n display: none;\r\n position: relative;\r\n top: auto;\r\n left: auto;\r\n right: auto;\r\n width: 100%;\r\n box-sizing: border-box;\r\n box-shadow: none;\r\n opacity: 1;\r\n visibility: visible;\r\n transform: none;\r\n }\r\n\r\n :host([inline]) .menu-dropdown.show {\r\n display: block;\r\n pointer-events: auto;\r\n }\r\n\r\n @media (prefers-reduced-motion: reduce) {\r\n .menu-dropdown,\r\n .arrow {\r\n transition: none;\r\n }\r\n }\r\n `;\r\n\r\n /** Trigger label text (source: `title`, renamed because `title` is the native tooltip\r\n * attribute). Leave empty to show only `slot=\"icon\"`. */\r\n @property({ type: String })\r\n label = '';\r\n\r\n /** Renders the trigger as an `<a href>`. With panel content the click toggles the panel (as in\r\n * the source); with none it navigates. */\r\n @property({ type: String })\r\n href = '';\r\n\r\n @property({ type: String, attribute: 'icon-position' })\r\n iconPosition: 'left' | 'right' = 'left';\r\n\r\n /** Inline `width`/`height` of the `slot=\"icon\"` wrapper (source: `iconSize`, default `1.2rem`). */\r\n @property({ type: String, attribute: 'icon-size' })\r\n iconSize = '1.2rem';\r\n\r\n /** Inline `color` of the `slot=\"icon\"` wrapper (source: `iconColor`). */\r\n @property({ type: String, attribute: 'icon-color' })\r\n iconColor = 'currentColor';\r\n\r\n /** Which edge of the trigger the panel lines up with. */\r\n @property({ type: String })\r\n align: 'left' | 'right' = 'left';\r\n\r\n /** `target` of an `href` trigger (source default `_self`). */\r\n @property({ type: String })\r\n target = '_self';\r\n\r\n /** Extra class(es) on the trigger (source: `className`; a `class` on `st-dropdown` styles the host). */\r\n @property({ type: String, attribute: 'trigger-class-name' })\r\n triggerClassName = '';\r\n\r\n /** Extra class(es) on the panel (source: `itemGroupClassName`). */\r\n @property({ type: String, attribute: 'item-group-class-name' })\r\n itemGroupClassName = '';\r\n\r\n /** Unused, as in the source (`itemClassName` is never read there); kept for prop parity. */\r\n @property({ type: String, attribute: 'item-class-name' })\r\n itemClassName = '';\r\n\r\n @property({ type: Boolean, reflect: true })\r\n disabled = false;\r\n\r\n /** Highlights the trigger as the current section (e.g. the current page is one of its items). */\r\n @property({ type: Boolean, reflect: true })\r\n active = false;\r\n\r\n /** Opens the panel in the flow under the trigger instead of floating over the page (see the\r\n * styles). `st-header` sets this on its own menu's dropdowns while its hamburger panel shows. */\r\n @property({ type: Boolean, reflect: true })\r\n inline = false;\r\n\r\n /** Whether the panel is showing. Follows user toggles; may also be set from outside. */\r\n @property({ type: Boolean, reflect: true })\r\n open = false;\r\n\r\n /** Whether the default slot has content — the source's `children` check. */\r\n @state() private hasContent = false;\r\n\r\n /** Whether `slot=\"icon\"` has content. */\r\n @state() private hasIcon = false;\r\n\r\n connectedCallback(): void {\r\n super.connectedCallback();\r\n document.addEventListener('mousedown', this.closeOnOutsideMouseDown);\r\n window.addEventListener('keydown', this.closeOnEscape);\r\n }\r\n\r\n disconnectedCallback(): void {\r\n document.removeEventListener('mousedown', this.closeOnOutsideMouseDown);\r\n window.removeEventListener('keydown', this.closeOnEscape);\r\n if (Dropdown.openDropdown === this) Dropdown.openDropdown = null;\r\n super.disconnectedCallback();\r\n }\r\n\r\n protected updated(changed: Map<string, unknown>): void {\r\n if (!changed.has('open')) return;\r\n if (this.open) {\r\n const previous = Dropdown.openDropdown;\r\n Dropdown.openDropdown = this;\r\n if (previous && previous !== this) previous.setOpen(false);\r\n } else if (Dropdown.openDropdown === this) {\r\n Dropdown.openDropdown = null;\r\n }\r\n }\r\n\r\n private setOpen(open: boolean): void {\r\n if (this.open === open) return;\r\n this.open = open;\r\n this.dispatchEvent(new CustomEvent<DropdownToggleEventDetail>('dropdown-toggle', { detail: { open }, bubbles: true, composed: true }));\r\n }\r\n\r\n private closeOnOutsideMouseDown = (event: Event): void => {\r\n if (this.open && !event.composedPath().includes(this)) this.setOpen(false);\r\n };\r\n\r\n private closeOnEscape = (event: KeyboardEvent): void => {\r\n if (!this.open || event.key !== 'Escape') return;\r\n const focusWasInside = event.composedPath().includes(this);\r\n this.setOpen(false);\r\n if (focusWasInside) this.trigger()?.focus();\r\n };\r\n\r\n private trigger(): HTMLElement | null {\r\n return this.renderRoot.querySelector<HTMLElement>('.menu-title');\r\n }\r\n\r\n /** Keyboard-reachable entries in the panel, in order: menu items (they delegate focus to their\r\n * own link) plus any plain links/buttons/tabbable elements a caller slots in. */\r\n private panelItems(): HTMLElement[] {\r\n const slot = this.renderRoot.querySelector<HTMLSlotElement>('.menu-dropdown slot');\r\n if (!slot) return [];\r\n const selector =\r\n 'st-menu-item, st-menu-grid-item, a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\r\n const items: HTMLElement[] = [];\r\n for (const el of slot.assignedElements({ flatten: true })) {\r\n if (el.matches(selector)) items.push(el as HTMLElement);\r\n items.push(...Array.from(el.querySelectorAll<HTMLElement>(selector)));\r\n }\r\n return items;\r\n }\r\n\r\n /** Moves focus into the panel once it has become visible (it is `visibility: hidden` until the\r\n * `show` class lands, and hidden elements can't take focus). */\r\n private focusItem(pick: (items: HTMLElement[]) => HTMLElement | undefined): void {\r\n void this.updateComplete.then(() => pick(this.panelItems())?.focus());\r\n }\r\n\r\n /** Enter/Space/ArrowDown open the panel and focus its first entry; ArrowUp its last. */\r\n private onTitleKeyDown = (event: KeyboardEvent): void => {\r\n if (!this.hasContent || this.disabled) return;\r\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n this.setOpen(true);\r\n this.focusItem((items) => items[0]);\r\n } else if (event.key === 'ArrowUp') {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n this.setOpen(true);\r\n this.focusItem((items) => items[items.length - 1]);\r\n }\r\n };\r\n\r\n /** Arrow keys/Home/End move between the panel's entries; Tab closes the panel and moves on. */\r\n private onPanelKeyDown = (event: KeyboardEvent): void => {\r\n if (event.key === 'Tab') {\r\n this.setOpen(false);\r\n return;\r\n }\r\n if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) return;\r\n const items = this.panelItems();\r\n if (items.length === 0) return;\r\n event.preventDefault();\r\n event.stopPropagation();\r\n const path = event.composedPath();\r\n const current = items.findIndex((item) => path.includes(item));\r\n const last = items.length - 1;\r\n const next =\r\n event.key === 'Home'\r\n ? 0\r\n : event.key === 'End'\r\n ? last\r\n : event.key === 'ArrowDown'\r\n ? current < 0 || current === last ? 0 : current + 1\r\n : current <= 0 ? last : current - 1;\r\n items[next].focus();\r\n };\r\n\r\n private onTitleClick = (event: Event): void => {\r\n event.stopPropagation();\r\n if (!this.hasContent) return;\r\n event.preventDefault();\r\n this.setOpen(!this.open);\r\n };\r\n\r\n /** A click anywhere inside the panel closes it, as in the source. */\r\n private onPanelClick = (): void => {\r\n this.setOpen(false);\r\n };\r\n\r\n private onContentSlotChange = (event: Event): void => {\r\n this.hasContent = (event.target as HTMLSlotElement).assignedNodes({ flatten: true }).some(\r\n (node) => node.nodeType === Node.ELEMENT_NODE || !!node.textContent?.trim()\r\n );\r\n };\r\n\r\n private onIconSlotChange = (event: Event): void => {\r\n this.hasIcon = (event.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0;\r\n };\r\n\r\n render() {\r\n const icon = html`<span\r\n class=\"icon ${this.iconPosition === 'left' ? 'icon-left' : 'icon-right'}\"\r\n ?hidden=${!this.hasIcon}\r\n style=\"width:${this.iconSize};height:${this.iconSize};color:${this.iconColor}\"\r\n ><slot name=\"icon\" @slotchange=${this.onIconSlotChange}></slot\r\n ></span>`;\r\n const titleContent = html`\r\n ${this.iconPosition === 'left' ? icon : nothing} ${this.label ? html`<span part=\"label\">${this.label}</span>` : nothing}\r\n ${this.iconPosition === 'right' ? icon : nothing}\r\n ${this.hasContent ? html`<span class=\"arrow\" part=\"caret\" aria-hidden=\"true\">▼</span>` : nothing}\r\n `;\r\n const titleClass = `menu-title ${this.triggerClassName} ${this.open ? 'active' : ''}`;\r\n\r\n return html`\r\n <div class=\"menu\" part=\"menu\">\r\n ${this.href\r\n ? html`\r\n <a\r\n part=\"trigger\"\r\n class=${titleClass}\r\n href=${this.href}\r\n target=${this.target}\r\n aria-haspopup=${this.hasContent ? 'true' : 'false'}\r\n aria-expanded=${this.open}\r\n aria-disabled=${this.disabled ? 'true' : nothing}\r\n @click=${this.onTitleClick}\r\n @keydown=${this.onTitleKeyDown}\r\n >\r\n ${titleContent}\r\n </a>\r\n `\r\n : html`\r\n <button\r\n part=\"trigger\"\r\n class=${titleClass}\r\n type=\"button\"\r\n aria-haspopup=\"true\"\r\n aria-expanded=${this.open}\r\n ?disabled=${this.disabled}\r\n @click=${this.onTitleClick}\r\n @keydown=${this.onTitleKeyDown}\r\n >\r\n ${titleContent}\r\n </button>\r\n `}\r\n <div\r\n part=\"panel\"\r\n class=\"menu-dropdown align-${this.align} ${this.itemGroupClassName} ${this.open ? 'show' : ''}\"\r\n role=\"menu\"\r\n @click=${this.onPanelClick}\r\n @keydown=${this.onPanelKeyDown}\r\n >\r\n <slot @slotchange=${this.onContentSlotChange}></slot>\r\n </div>\r\n </div>\r\n `;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}