@starwind-ui/core 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import { join } from "node:path";
3
- import { fileURLToPath } from "node:url";
2
+ import { join } from "path";
3
+ import { fileURLToPath } from "url";
4
4
 
5
5
  // src/registry.json
6
6
  var registry_default = {
@@ -14,7 +14,7 @@ var registry_default = {
14
14
  { name: "button", type: "component", version: "2.0.1", dependencies: [] },
15
15
  { name: "card", type: "component", version: "1.1.0", dependencies: [] },
16
16
  { name: "checkbox", type: "component", version: "1.2.0", dependencies: [] },
17
- { name: "dialog", type: "component", version: "1.1.3", dependencies: [] },
17
+ { name: "dialog", type: "component", version: "1.2.0", dependencies: [] },
18
18
  { name: "dropdown", type: "component", version: "1.0.3", dependencies: [] },
19
19
  { name: "dropzone", type: "component", version: "1.0.0", dependencies: [] },
20
20
  { name: "input", type: "component", version: "1.1.1", dependencies: [] },
@@ -22,7 +22,7 @@ var registry_default = {
22
22
  { name: "pagination", type: "component", version: "2.0.1", dependencies: [] },
23
23
  { name: "progress", type: "component", version: "1.0.0", dependencies: [] },
24
24
  { name: "radio-group", type: "component", version: "1.0.0", dependencies: [] },
25
- { name: "select", type: "component", version: "1.3.2", dependencies: [] },
25
+ { name: "select", type: "component", version: "1.4.0", dependencies: [] },
26
26
  { name: "skeleton", type: "component", version: "1.0.0", dependencies: [] },
27
27
  { name: "switch", type: "component", version: "1.1.0", dependencies: [] },
28
28
  { name: "table", type: "component", version: "1.0.0", dependencies: [] },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/registry.json"],"sourcesContent":["import { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport componentRegistry from \"./registry.json\" with { type: \"json\" };\n\n/**\n * Component metadata interface describing a Starwind UI component\n */\nexport interface ComponentMeta {\n name: string;\n version: string;\n type: \"component\";\n dependencies: string[];\n}\n\n/**\n * Registry interface containing all available components\n */\nexport interface Registry {\n components: ComponentMeta[];\n}\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\n\n/**\n * Get the absolute path to a component file\n * @param {string} componentName - The name of the component\n * @param {string} fileName - The name of the file within the component\n * @returns {string} The absolute path to the component file\n */\nexport const getComponentPath = (componentName: string, fileName: string): string => {\n // In production (when installed as a dependency), the components will be in dist/src/components\n // In development, they will be in src/components\n const componentsDir = __dirname.includes(\"dist\") ? \"src/components\" : \"src/components\";\n return join(__dirname, componentsDir, componentName, fileName);\n};\n\n/**\n * Map of all components and their metadata from registry\n */\nexport const registry = componentRegistry.components as ComponentMeta[];\n","{\n \"$schema\": \"https://starwind.dev/registry-schema.json\",\n \"components\": [\n { \"name\": \"accordion\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"alert\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"avatar\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"badge\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"breadcrumb\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"button\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n { \"name\": \"card\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"checkbox\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"dialog\", \"type\": \"component\", \"version\": \"1.1.3\", \"dependencies\": [] },\n { \"name\": \"dropdown\", \"type\": \"component\", \"version\": \"1.0.3\", \"dependencies\": [] },\n { \"name\": \"dropzone\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"input\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"label\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"pagination\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n { \"name\": \"progress\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"radio-group\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"select\", \"type\": \"component\", \"version\": \"1.3.2\", \"dependencies\": [] },\n { \"name\": \"skeleton\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"switch\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"table\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"tabs\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"textarea\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"tooltip\", \"type\": \"component\", \"version\": \"1.1.2\", \"dependencies\": [] }\n ]\n}\n"],"mappings":";AAAA,SAAS,YAAY;AACrB,SAAS,qBAAqB;;;ACD9B;AAAA,EACE,SAAW;AAAA,EACX,YAAc;AAAA,IACZ,EAAE,MAAQ,aAAa,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACnF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,cAAc,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACpF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,QAAQ,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC9E,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,cAAc,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACpF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,eAAe,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACrF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,QAAQ,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC9E,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,WAAW,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,EACnF;AACF;;;ADLA,IAAM,YAAY,cAAc,IAAI,IAAI,KAAK,YAAY,GAAG,CAAC;AAQtD,IAAM,mBAAmB,CAAC,eAAuB,aAA6B;AAGnF,QAAM,gBAAgB,UAAU,SAAS,MAAM,IAAI,mBAAmB;AACtE,SAAO,KAAK,WAAW,eAAe,eAAe,QAAQ;AAC/D;AAKO,IAAM,WAAW,iBAAkB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/registry.json"],"sourcesContent":["import { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport componentRegistry from \"./registry.json\" with { type: \"json\" };\n\n/**\n * Component metadata interface describing a Starwind UI component\n */\nexport interface ComponentMeta {\n name: string;\n version: string;\n type: \"component\";\n dependencies: string[];\n}\n\n/**\n * Registry interface containing all available components\n */\nexport interface Registry {\n components: ComponentMeta[];\n}\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\n\n/**\n * Get the absolute path to a component file\n * @param {string} componentName - The name of the component\n * @param {string} fileName - The name of the file within the component\n * @returns {string} The absolute path to the component file\n */\nexport const getComponentPath = (componentName: string, fileName: string): string => {\n // In production (when installed as a dependency), the components will be in dist/src/components\n // In development, they will be in src/components\n const componentsDir = __dirname.includes(\"dist\") ? \"src/components\" : \"src/components\";\n return join(__dirname, componentsDir, componentName, fileName);\n};\n\n/**\n * Map of all components and their metadata from registry\n */\nexport const registry = componentRegistry.components as ComponentMeta[];\n","{\n \"$schema\": \"https://starwind.dev/registry-schema.json\",\n \"components\": [\n { \"name\": \"accordion\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"alert\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"avatar\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"badge\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"breadcrumb\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"button\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n { \"name\": \"card\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"checkbox\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"dialog\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"dropdown\", \"type\": \"component\", \"version\": \"1.0.3\", \"dependencies\": [] },\n { \"name\": \"dropzone\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"input\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"label\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"pagination\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n { \"name\": \"progress\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"radio-group\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"select\", \"type\": \"component\", \"version\": \"1.4.0\", \"dependencies\": [] },\n { \"name\": \"skeleton\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"switch\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"table\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"tabs\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"textarea\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n { \"name\": \"tooltip\", \"type\": \"component\", \"version\": \"1.1.2\", \"dependencies\": [] }\n ]\n}\n"],"mappings":";AAAA,SAAS,YAAY;AACrB,SAAS,qBAAqB;;;ACD9B;AAAA,EACE,SAAW;AAAA,EACX,YAAc;AAAA,IACZ,EAAE,MAAQ,aAAa,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACnF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,cAAc,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACpF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,QAAQ,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC9E,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,cAAc,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACpF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,eAAe,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACrF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,QAAQ,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC9E,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,WAAW,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,EACnF;AACF;;;ADLA,IAAM,YAAY,cAAc,IAAI,IAAI,KAAK,YAAY,GAAG,CAAC;AAQtD,IAAM,mBAAmB,CAAC,eAAuB,aAA6B;AAGnF,QAAM,gBAAgB,UAAU,SAAS,MAAM,IAAI,mBAAmB;AACtE,SAAO,KAAK,WAAW,eAAe,eAAe,QAAQ;AAC/D;AAKO,IAAM,WAAW,iBAAkB;","names":[]}
@@ -12,10 +12,11 @@ const { class: className, ...rest } = Astro.props;
12
12
 
13
13
  <script>
14
14
  class DialogHandler {
15
- private trigger: HTMLButtonElement;
15
+ private triggers: HTMLButtonElement[] = [];
16
16
  private dialog: HTMLDialogElement;
17
- private closeButtons: NodeListOf<HTMLButtonElement>;
17
+ private closeButtons: HTMLButtonElement[] = [];
18
18
  private backdrop: HTMLElement;
19
+ private dialogId: string;
19
20
  /**
20
21
  * The duration of the animation in milliseconds. This is used to calculate the
21
22
  * duration of close animation before hiding the dialog and backdrop
@@ -26,6 +27,14 @@ const { class: className, ...rest } = Astro.props;
26
27
  this.dialog = dialogWrapper.querySelector("dialog")!;
27
28
  this.backdrop = dialogWrapper.querySelector(".starwind-dialog-backdrop")!;
28
29
 
30
+ // if no ID was provided for the wrapper, generate one
31
+ if (dialogWrapper.id) {
32
+ this.dialogId = dialogWrapper.id;
33
+ } else {
34
+ this.dialogId = `starwind-dialog${dialogNumber}`;
35
+ dialogWrapper.id = this.dialogId;
36
+ }
37
+
29
38
  // animationDuration is set with inline styles through passed prop to DialogContent
30
39
  const animationDurationString = this.dialog.style.animationDuration;
31
40
  if (animationDurationString.endsWith("ms")) {
@@ -37,13 +46,25 @@ const { class: className, ...rest } = Astro.props;
37
46
  this.animationDuration = 200;
38
47
  }
39
48
 
40
- // if trigger is set with asChild, use the first child element for trigger button
41
- const tempTrigger = dialogWrapper.querySelector(".starwind-dialog-trigger") as HTMLElement;
42
- if (tempTrigger?.hasAttribute("data-as-child")) {
43
- this.trigger = tempTrigger.firstElementChild as HTMLButtonElement;
44
- } else {
45
- this.trigger = tempTrigger as HTMLButtonElement;
46
- }
49
+ // Find internal triggers and handle them
50
+ const internalTriggers = dialogWrapper.querySelectorAll(".starwind-dialog-trigger");
51
+ internalTriggers.forEach((triggerElement) => {
52
+ const tempTrigger = triggerElement as HTMLElement;
53
+ let trigger: HTMLButtonElement;
54
+
55
+ if (tempTrigger?.hasAttribute("data-as-child")) {
56
+ trigger = tempTrigger.firstElementChild as HTMLButtonElement;
57
+ } else {
58
+ trigger = tempTrigger as HTMLButtonElement;
59
+ }
60
+
61
+ if (trigger) {
62
+ this.triggers.push(trigger);
63
+ }
64
+ });
65
+
66
+ // Find external triggers that target this dialog
67
+ this.findExternalTriggers();
47
68
 
48
69
  // if closeButtons are set with asChild, swap the wrapper with its first child
49
70
  const tempCloseButtons = dialogWrapper.querySelectorAll(
@@ -60,12 +81,13 @@ const { class: className, ...rest } = Astro.props;
60
81
  return button;
61
82
  });
62
83
 
63
- this.closeButtons = dialogWrapper.querySelectorAll(
64
- ".starwind-dialog-close",
65
- ) as NodeListOf<HTMLButtonElement>;
84
+ // Convert NodeList to Array for consistency with triggers
85
+ this.closeButtons = Array.from(
86
+ dialogWrapper.querySelectorAll(".starwind-dialog-close"),
87
+ ) as HTMLButtonElement[];
66
88
 
67
- // if any elements are not there, exit
68
- if (!this.trigger || !this.dialog || !this.backdrop) return;
89
+ // if essential elements are not there, exit
90
+ if (!this.dialog || !this.backdrop) return;
69
91
 
70
92
  this.setupAccessibility(dialogNumber);
71
93
  this.setupEvents();
@@ -82,9 +104,40 @@ const { class: className, ...rest } = Astro.props;
82
104
  }
83
105
  }
84
106
 
107
+ /**
108
+ * Find all external triggers that target this dialog
109
+ */
110
+ private findExternalTriggers(): void {
111
+ const externalTriggers = document.querySelectorAll(
112
+ `.starwind-dialog-trigger[data-dialog-for="${this.dialogId}"]`,
113
+ );
114
+
115
+ externalTriggers.forEach((triggerElement) => {
116
+ // Skip if this is an internal trigger we already processed
117
+ const dialogWrapper = triggerElement.closest(".starwind-dialog");
118
+ if (dialogWrapper && dialogWrapper.id === this.dialogId) {
119
+ return;
120
+ }
121
+
122
+ let trigger: HTMLButtonElement;
123
+ if (triggerElement.hasAttribute("data-as-child")) {
124
+ trigger = triggerElement.firstElementChild as HTMLButtonElement;
125
+ } else {
126
+ trigger = triggerElement as HTMLButtonElement;
127
+ }
128
+
129
+ if (trigger && !this.triggers.includes(trigger)) {
130
+ this.triggers.push(trigger);
131
+ }
132
+ });
133
+ }
134
+
85
135
  private setupEvents(): void {
86
- this.trigger?.addEventListener("click", () => {
87
- this.open();
136
+ // Add click listeners to all triggers
137
+ this.triggers.forEach((trigger) => {
138
+ trigger.addEventListener("click", () => {
139
+ this.open();
140
+ });
88
141
  });
89
142
 
90
143
  // Add click handlers to all close buttons
@@ -189,3 +242,9 @@ const { class: className, ...rest } = Astro.props;
189
242
  setupDialogs();
190
243
  document.addEventListener("astro:after-swap", setupDialogs);
191
244
  </script>
245
+
246
+ <style>
247
+ .overflow-hidden {
248
+ overflow: hidden;
249
+ }
250
+ </style>
@@ -6,9 +6,14 @@ type Props = HTMLAttributes<"button"> & {
6
6
  * When true, the component will render its child element with a simple wrapper instead of a button component
7
7
  */
8
8
  asChild?: boolean;
9
+ /**
10
+ * Optional ID of the dialog to trigger. If not provided and the trigger is inside a Dialog component,
11
+ * it will automatically target that dialog. Required when used outside a Dialog component.
12
+ */
13
+ for?: string;
9
14
  };
10
15
 
11
- const { class: className, asChild = false, ...rest } = Astro.props;
16
+ const { class: className, asChild = false, for: dialogFor, ...rest } = Astro.props;
12
17
 
13
18
  // Get the first child element if asChild is true
14
19
  let hasChildren = false;
@@ -19,7 +24,7 @@ if (Astro.slots.has("default")) {
19
24
 
20
25
  {
21
26
  asChild && hasChildren ? (
22
- <div class="starwind-dialog-trigger" data-as-child>
27
+ <div class="starwind-dialog-trigger" data-as-child data-dialog-for={dialogFor}>
23
28
  <slot />
24
29
  </div>
25
30
  ) : (
@@ -27,6 +32,7 @@ if (Astro.slots.has("default")) {
27
32
  type="button"
28
33
  aria-haspopup="dialog"
29
34
  class:list={["starwind-dialog-trigger", className]}
35
+ data-dialog-for={dialogFor}
30
36
  {...rest}
31
37
  >
32
38
  <slot />
@@ -3,14 +3,14 @@ import type { HTMLAttributes } from "astro/types";
3
3
 
4
4
  type Props = HTMLAttributes<"div"> & {
5
5
  /**
6
- * The name of the select field for form handling
6
+ * The name for the hidden <select> element - used for standard form handling
7
7
  */
8
8
  name?: string;
9
9
  /**
10
10
  * The value of the item that should be selected by default
11
11
  */
12
12
  defaultValue?: string;
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+
14
14
  children: any;
15
15
  };
16
16
 
@@ -27,7 +27,7 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
27
27
  </div>
28
28
 
29
29
  <script>
30
- import type { SelectChangeEvent } from "./SelectTypes";
30
+ import type { SelectChangeEvent, SelectEvent } from "./SelectTypes";
31
31
 
32
32
  class SelectHandler {
33
33
  private select: HTMLElement;
@@ -73,7 +73,10 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
73
73
  selectField.tabIndex = -1;
74
74
  selectField.setAttribute("aria-hidden", "true");
75
75
  selectField.setAttribute("placeholder", "select");
76
- selectField.name = this.select.getAttribute("data-name") || "";
76
+ const selectName = this.select.getAttribute("data-name");
77
+ if (selectName) {
78
+ selectField.name = selectName;
79
+ }
77
80
 
78
81
  // you can comment out this "sr-only" class line below if you want to see the native select in action
79
82
  selectField.classList.add("starwind-sr-only");
@@ -237,6 +240,22 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
237
240
 
238
241
  // passive resize listener to call setSize()
239
242
  window.addEventListener("resize", () => this.setSize(), { passive: true });
243
+
244
+ // Listen for programmatic selection events
245
+ document.addEventListener("starwind-select:select", (e: Event) => {
246
+ const selectEvent = e as SelectEvent;
247
+ const selectId = selectEvent.detail.selectId;
248
+ const selectName = selectEvent.detail.selectName;
249
+ const selectValue = selectEvent.detail.value;
250
+
251
+ // Check if this event is for this select
252
+ if (
253
+ (selectId && this.select.id === selectId) ||
254
+ (selectName && this.select.getAttribute("data-name") === selectName)
255
+ ) {
256
+ this.programmaticallySelect(selectValue);
257
+ }
258
+ });
240
259
  }
241
260
 
242
261
  private handleNavigationKeys(e: KeyboardEvent) {
@@ -438,6 +457,27 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
438
457
  }
439
458
  }
440
459
 
460
+ /**
461
+ * Programmatically selects an option by value
462
+ */
463
+ private programmaticallySelect(value: string): void {
464
+ if (!this.content) return;
465
+
466
+ const item = this.content.querySelector(`[data-value="${value}"]`);
467
+ if (item instanceof HTMLElement) {
468
+ this.returnFocusOnClose = false;
469
+
470
+ // Update aria-selected attributes immediately
471
+ if (this.selectedItem) {
472
+ this.selectedItem.setAttribute("aria-selected", "false");
473
+ }
474
+ item.setAttribute("aria-selected", "true");
475
+
476
+ // Then call handleSelection which will update the rest
477
+ this.handleSelection(item);
478
+ }
479
+ }
480
+
441
481
  /**
442
482
  * TODO: add position logic to avoid collisions with window boundary
443
483
  * It will need to switch to top or bottom depending on space available
@@ -5,3 +5,9 @@ export interface SelectChangeEvent extends CustomEvent {
5
5
  label: string;
6
6
  };
7
7
  }
8
+
9
+ export interface SelectEvent extends CustomEvent {
10
+ detail:
11
+ | { value: string; selectId: string; selectName?: string }
12
+ | { value: string; selectId?: string; selectName: string };
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starwind-ui/core",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Starwind UI core components and registry",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -28,11 +28,11 @@
28
28
  "dist"
29
29
  ],
30
30
  "devDependencies": {
31
- "astro": "^5.0.0",
32
- "fs-extra": "^11.2.0",
33
- "glob": "^10.3.10",
34
- "tailwindcss": "^4.0.0",
35
- "tsup": "^8.0.2"
31
+ "astro": "5.11.0",
32
+ "fs-extra": "11.3.0",
33
+ "glob": "11.0.3",
34
+ "tailwindcss": "4.1.11",
35
+ "tsup": "8.5.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "pnpm clean && tsup",