popup-button-plugin 0.1.2 → 0.1.3

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.
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var PopupButtonPlugin = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ PopupButton: () => PopupButton,
25
+ getButtonContent: () => getButtonContent,
26
+ registerButtonContent: () => registerButtonContent,
27
+ unregisterButtonContent: () => unregisterButtonContent
28
+ });
29
+
30
+ // src/popup-registry.ts
31
+ var registry = /* @__PURE__ */ new Map();
32
+ function registerButtonContent(name, content) {
33
+ registry.set(name, content);
34
+ }
35
+ function getButtonContent(name) {
36
+ return registry.get(name);
37
+ }
38
+ function unregisterButtonContent(name) {
39
+ registry.delete(name);
40
+ }
41
+
42
+ // src/popup-button.ts
43
+ var PopupButton = class extends HTMLElement {
44
+ constructor() {
45
+ super();
46
+ // Tracks whether the popup is currently visible.
47
+ this.isOpen = false;
48
+ /** Runs when the visible <button> inside our shadow DOM is clicked. */
49
+ this.handleButtonClick = () => {
50
+ const found = getButtonContent(this.buttonName);
51
+ if (!found) {
52
+ this.dispatchEvent(
53
+ new CustomEvent("popup-content-missing", {
54
+ detail: { name: this.buttonName },
55
+ bubbles: true
56
+ })
57
+ );
58
+ return;
59
+ }
60
+ this.content = found;
61
+ this.isOpen = true;
62
+ this.render();
63
+ };
64
+ /** Runs when the popup's close button or backdrop is clicked. */
65
+ this.handleClose = () => {
66
+ this.isOpen = false;
67
+ this.render();
68
+ };
69
+ this.attachShadow({ mode: "open" });
70
+ }
71
+ /**
72
+ * Runs once, the first time this tag is attached to the page.
73
+ * (Equivalent to a component's "mount"/"componentDidMount".)
74
+ */
75
+ connectedCallback() {
76
+ this.render();
77
+ }
78
+ /** Reads the `name` HTML attribute, e.g. <popup-button name="save-info">. */
79
+ get buttonName() {
80
+ return this.getAttribute("name") ?? "";
81
+ }
82
+ /**
83
+ * Reads the `label` HTML attribute (button text), with a default.
84
+ *
85
+ * NOTE: named `buttonLabel`, NOT `label` — React 19 sets custom
86
+ * element props as real DOM properties (`el.label = '...'`) when
87
+ * the property exists on the element. A getter-only `label` here
88
+ * (no setter) would make that assignment throw
89
+ * "Cannot set property label ... which has only a getter" and
90
+ * crash the whole React render. Avoid naming internal accessors
91
+ * the same as attributes a framework might try to set directly.
92
+ */
93
+ get buttonLabel() {
94
+ return this.getAttribute("label") ?? "Click me";
95
+ }
96
+ /**
97
+ * Rebuilds this element's shadow DOM to match current state
98
+ * (isOpen / content). There's no automatic re-render like Stencil —
99
+ * we call this ourselves every time state changes.
100
+ */
101
+ render() {
102
+ const shadow = this.shadowRoot;
103
+ shadow.innerHTML = `
104
+ <style>${STYLES}</style>
105
+ <button class="btn">${escapeHtml(this.buttonLabel)}</button>
106
+ ${this.isOpen ? this.renderPopupMarkup() : ""}
107
+ `;
108
+ shadow.querySelector(".btn").addEventListener("click", this.handleButtonClick);
109
+ if (this.isOpen) {
110
+ shadow.querySelector(".backdrop").addEventListener("click", this.handleClose);
111
+ shadow.querySelector(".close").addEventListener("click", this.handleClose);
112
+ shadow.querySelector(".popup").addEventListener("click", (e) => e.stopPropagation());
113
+ }
114
+ }
115
+ renderPopupMarkup() {
116
+ const title = this.content?.title ?? "";
117
+ const body = this.content?.body ?? "";
118
+ return `
119
+ <div class="backdrop">
120
+ <div class="popup">
121
+ <div class="popup__header">
122
+ <span class="popup__title">${escapeHtml(title)}</span>
123
+ <button class="close" aria-label="Close">&times;</button>
124
+ </div>
125
+ <div class="popup__body">${escapeHtml(body)}</div>
126
+ </div>
127
+ </div>
128
+ `;
129
+ }
130
+ };
131
+ function escapeHtml(value) {
132
+ const div = document.createElement("div");
133
+ div.textContent = value;
134
+ return div.innerHTML;
135
+ }
136
+ var STYLES = `
137
+ :host { display: inline-block; font-family: inherit; }
138
+ .btn {
139
+ padding: 8px 16px;
140
+ border-radius: 6px;
141
+ border: none;
142
+ background: #2563eb;
143
+ color: #fff;
144
+ font-size: 14px;
145
+ cursor: pointer;
146
+ }
147
+ .btn:hover { background: #1d4ed8; }
148
+ .backdrop {
149
+ position: fixed; inset: 0;
150
+ background: rgba(0,0,0,0.5);
151
+ display: flex; align-items: center; justify-content: center;
152
+ z-index: 1000;
153
+ }
154
+ .popup {
155
+ background: #fff; border-radius: 10px;
156
+ min-width: 320px; max-width: 90vw;
157
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
158
+ }
159
+ .popup__header {
160
+ display: flex; align-items: center; justify-content: space-between;
161
+ padding: 16px; border-bottom: 1px solid #e5e7eb;
162
+ }
163
+ .popup__title { font-size: 16px; font-weight: 700; color: #111827; }
164
+ .close { background: none; border: none; font-size: 20px; cursor: pointer; color: #6b7280; }
165
+ .popup__body { padding: 16px; color: #374151; }
166
+ `;
167
+ customElements.define("popup-button", PopupButton);
168
+ return __toCommonJS(index_exports);
169
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "popup-button-plugin",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A tiny, standalone, framework-free button-with-popup web component: register content by name, and <popup-button name=\"...\"> automatically shows it when clicked.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -9,13 +9,24 @@
9
9
  "dist/"
10
10
  ],
11
11
  "scripts": {
12
- "build": "tsc -p tsconfig.json",
12
+ "build": "tsc -p tsconfig.json && npm run build:global",
13
+ "build:global": "esbuild src/index.ts --bundle --format=iife --global-name=PopupButtonPlugin --outfile=dist/popup-button-plugin.global.js",
13
14
  "start": "tsc -p tsconfig.json --watch",
14
15
  "prepublishOnly": "npm run build"
15
16
  },
16
- "keywords": ["web-components", "custom-elements", "popup", "modal", "button"],
17
+ "keywords": [
18
+ "web-components",
19
+ "custom-elements",
20
+ "popup",
21
+ "modal",
22
+ "button"
23
+ ],
17
24
  "license": "MIT",
18
25
  "devDependencies": {
26
+ "esbuild": "^0.28.2",
19
27
  "typescript": "^5.6.0"
28
+ },
29
+ "allowScripts": {
30
+ "esbuild@0.28.2": true
20
31
  }
21
32
  }