@appbuildersph/badge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @appbuildersph/badge
2
+
3
+ Reserved for a future phase — not yet implemented. See the root README's "Next phases" list.
@@ -0,0 +1,46 @@
1
+ var AppBuildersBadge=(function(exports){'use strict';var g={"bottom-right":"bottom:16px;right:16px;","bottom-left":"bottom:16px;left:16px;","top-right":"top:16px;right:16px;","top-left":"top:16px;left:16px;"};function m(e){return e!=="system"?e:typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":"dark"}function p(e,t){let n=m(e.theme),d=[t.appName,t.organization,t.environment].filter(Boolean).join(" \xB7 ");return `
2
+ <style>
3
+ :host { all: initial; }
4
+ .ab-badge {
5
+ position: fixed;
6
+ ${g[e.position]}
7
+ z-index: 2147483647;
8
+ display: flex;
9
+ align-items: center;
10
+ gap: 6px;
11
+ padding: 6px 10px;
12
+ border-radius: 999px;
13
+ font-family: system-ui, -apple-system, sans-serif;
14
+ font-size: 12px;
15
+ line-height: 1.4;
16
+ background: ${n==="dark"?"#111":"#fff"};
17
+ color: ${n==="dark"?"#fff":"#111"};
18
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
19
+ cursor: ${e.expandable?"pointer":"default"};
20
+ transition: ${e.animation?"all 0.2s ease":"none"};
21
+ user-select: none;
22
+ }
23
+ .ab-badge__dot {
24
+ width: 6px;
25
+ height: 6px;
26
+ border-radius: 50%;
27
+ background: ${t.verified?"#22c55e":"#94a3b8"};
28
+ flex-shrink: 0;
29
+ }
30
+ .ab-badge__details {
31
+ display: none;
32
+ margin-left: 4px;
33
+ opacity: 0.75;
34
+ white-space: nowrap;
35
+ }
36
+ .ab-badge--expanded .ab-badge__details {
37
+ display: inline;
38
+ }
39
+ </style>
40
+ <div class="ab-badge" part="badge">
41
+ <span class="ab-badge__dot"></span>
42
+ <span>Verified by App Builders PH</span>
43
+ <span class="ab-badge__details">${d}</span>
44
+ </div>
45
+ `}var i="appbuilders-badge",r=false;function s(){if(r||typeof window>"u"||typeof HTMLElement>"u")return;if(customElements.get(i)){r=true;return}class e extends HTMLElement{shadow;constructor(){super(),this.shadow=this.attachShadow({mode:"open"});}setConfig(n,d){this.shadow.innerHTML=p(n,d),n.expandable&&this.shadow.querySelector(".ab-badge")?.addEventListener("click",f=>{f.currentTarget.classList.toggle("ab-badge--expanded");});}}customElements.define(i,e),r=true;}var o={theme:"system",position:"bottom-right",animation:true,expandable:true};function a(e){return e===false?null:e===void 0||e===true?{...o}:e.enabled===false?null:{theme:e.theme??o.theme,position:e.position??o.position,animation:e.animation??o.animation,expandable:e.expandable??o.expandable}}var l=class{identity;config;el=null;constructor(t,n){this.identity=t,this.config=a(n);}isMounted(){return this.el!==null}mount(t){if(typeof document>"u"||this.config===null||this.el)return;s();let n=document.createElement(i);n.setConfig(this.config,this.identity),(t??document.body).appendChild(n),this.el=n;}unmount(){this.el&&(this.el.remove(),this.el=null);}update(t){if(this.config=a(t),!!this.el){if(this.config===null){this.unmount();return}this.el.setConfig(this.config,this.identity);}}};exports.BADGE_TAG=i;exports.Badge=l;exports.DEFAULT_BADGE_CONFIG=o;exports.defineBadgeElement=s;exports.resolveBadgeConfig=a;return exports;})({});//# sourceMappingURL=appbuilders-badge.global.js.map
46
+ //# sourceMappingURL=appbuilders-badge.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/template.ts","../src/element.ts","../src/types.ts","../src/badge.ts"],"names":["POSITION_STYLES","resolveTheme","theme","template","config","identity","details","BADGE_TAG","registered","defineBadgeElement","AppBuildersBadgeElement","event","DEFAULT_BADGE_CONFIG","resolveBadgeConfig","input","Badge","target","el"],"mappings":"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","file":"appbuilders-badge.global.js","sourcesContent":["import type { BadgeIdentityInfo, ResolvedBadgeConfig } from \"./types.js\";\n\nconst POSITION_STYLES: Record<ResolvedBadgeConfig[\"position\"], string> = {\n \"bottom-right\": \"bottom:16px;right:16px;\",\n \"bottom-left\": \"bottom:16px;left:16px;\",\n \"top-right\": \"top:16px;right:16px;\",\n \"top-left\": \"top:16px;left:16px;\",\n};\n\nfunction resolveTheme(theme: ResolvedBadgeConfig[\"theme\"]): \"dark\" | \"light\" {\n if (theme !== \"system\") return theme;\n if (typeof window !== \"undefined\" && window.matchMedia) {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n }\n return \"dark\";\n}\n\nexport function template(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): string {\n const theme = resolveTheme(config.theme);\n const details = [identity.appName, identity.organization, identity.environment]\n .filter(Boolean)\n .join(\" · \");\n\n return `\n <style>\n :host { all: initial; }\n .ab-badge {\n position: fixed;\n ${POSITION_STYLES[config.position]}\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-radius: 999px;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n line-height: 1.4;\n background: ${theme === \"dark\" ? \"#111\" : \"#fff\"};\n color: ${theme === \"dark\" ? \"#fff\" : \"#111\"};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);\n cursor: ${config.expandable ? \"pointer\" : \"default\"};\n transition: ${config.animation ? \"all 0.2s ease\" : \"none\"};\n user-select: none;\n }\n .ab-badge__dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${identity.verified ? \"#22c55e\" : \"#94a3b8\"};\n flex-shrink: 0;\n }\n .ab-badge__details {\n display: none;\n margin-left: 4px;\n opacity: 0.75;\n white-space: nowrap;\n }\n .ab-badge--expanded .ab-badge__details {\n display: inline;\n }\n </style>\n <div class=\"ab-badge\" part=\"badge\">\n <span class=\"ab-badge__dot\"></span>\n <span>Verified by App Builders PH</span>\n <span class=\"ab-badge__details\">${details}</span>\n </div>\n `;\n}\n","import { template } from \"./template.js\";\nimport type { BadgeIdentityInfo, ResolvedBadgeConfig } from \"./types.js\";\n\nexport const BADGE_TAG = \"appbuilders-badge\";\n\nlet registered = false;\n\nexport function defineBadgeElement(): void {\n if (registered) return;\n if (typeof window === \"undefined\" || typeof HTMLElement === \"undefined\") return;\n if (customElements.get(BADGE_TAG)) {\n registered = true;\n return;\n }\n\n class AppBuildersBadgeElement extends HTMLElement {\n private readonly shadow: ShadowRoot;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void {\n this.shadow.innerHTML = template(config, identity);\n\n if (config.expandable) {\n this.shadow.querySelector(\".ab-badge\")?.addEventListener(\"click\", (event) => {\n (event.currentTarget as HTMLElement).classList.toggle(\"ab-badge--expanded\");\n });\n }\n }\n }\n\n customElements.define(BADGE_TAG, AppBuildersBadgeElement);\n registered = true;\n}\n\nexport interface BadgeElement extends HTMLElement {\n setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void;\n}\n","import type { BadgeConfig, BadgePosition, Environment, Theme } from \"@appbuildersph/shared\";\n\nexport interface BadgeIdentityInfo {\n appName: string;\n organization?: string;\n developer?: string;\n environment?: Environment;\n verified?: boolean;\n}\n\nexport interface ResolvedBadgeConfig {\n theme: Theme;\n position: BadgePosition;\n animation: boolean;\n expandable: boolean;\n}\n\nexport const DEFAULT_BADGE_CONFIG: ResolvedBadgeConfig = {\n theme: \"system\",\n position: \"bottom-right\",\n animation: true,\n expandable: true,\n};\n\nexport function resolveBadgeConfig(input?: boolean | BadgeConfig): ResolvedBadgeConfig | null {\n if (input === false) return null;\n if (input === undefined || input === true) return { ...DEFAULT_BADGE_CONFIG };\n if (input.enabled === false) return null;\n\n return {\n theme: input.theme ?? DEFAULT_BADGE_CONFIG.theme,\n position: input.position ?? DEFAULT_BADGE_CONFIG.position,\n animation: input.animation ?? DEFAULT_BADGE_CONFIG.animation,\n expandable: input.expandable ?? DEFAULT_BADGE_CONFIG.expandable,\n };\n}\n","import type { BadgeConfig } from \"@appbuildersph/shared\";\nimport { BADGE_TAG, defineBadgeElement, type BadgeElement } from \"./element.js\";\nimport { resolveBadgeConfig, type BadgeIdentityInfo, type ResolvedBadgeConfig } from \"./types.js\";\n\nexport class Badge {\n private readonly identity: BadgeIdentityInfo;\n private config: ResolvedBadgeConfig | null;\n private el: BadgeElement | null = null;\n\n constructor(identity: BadgeIdentityInfo, config?: boolean | BadgeConfig) {\n this.identity = identity;\n this.config = resolveBadgeConfig(config);\n }\n\n isMounted(): boolean {\n return this.el !== null;\n }\n\n mount(target?: Element): void {\n if (typeof document === \"undefined\") return;\n if (this.config === null) return;\n if (this.el) return;\n\n defineBadgeElement();\n const el = document.createElement(BADGE_TAG) as BadgeElement;\n el.setConfig(this.config, this.identity);\n (target ?? document.body).appendChild(el);\n this.el = el;\n }\n\n unmount(): void {\n if (!this.el) return;\n this.el.remove();\n this.el = null;\n }\n\n update(config?: boolean | BadgeConfig): void {\n this.config = resolveBadgeConfig(config);\n\n if (!this.el) return;\n if (this.config === null) {\n this.unmount();\n return;\n }\n this.el.setConfig(this.config, this.identity);\n }\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ // src/template.ts
4
+ var POSITION_STYLES = {
5
+ "bottom-right": "bottom:16px;right:16px;",
6
+ "bottom-left": "bottom:16px;left:16px;",
7
+ "top-right": "top:16px;right:16px;",
8
+ "top-left": "top:16px;left:16px;"
9
+ };
10
+ function resolveTheme(theme) {
11
+ if (theme !== "system") return theme;
12
+ if (typeof window !== "undefined" && window.matchMedia) {
13
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
14
+ }
15
+ return "dark";
16
+ }
17
+ function template(config, identity) {
18
+ const theme = resolveTheme(config.theme);
19
+ const details = [identity.appName, identity.organization, identity.environment].filter(Boolean).join(" \xB7 ");
20
+ return `
21
+ <style>
22
+ :host { all: initial; }
23
+ .ab-badge {
24
+ position: fixed;
25
+ ${POSITION_STYLES[config.position]}
26
+ z-index: 2147483647;
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 6px;
30
+ padding: 6px 10px;
31
+ border-radius: 999px;
32
+ font-family: system-ui, -apple-system, sans-serif;
33
+ font-size: 12px;
34
+ line-height: 1.4;
35
+ background: ${theme === "dark" ? "#111" : "#fff"};
36
+ color: ${theme === "dark" ? "#fff" : "#111"};
37
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
38
+ cursor: ${config.expandable ? "pointer" : "default"};
39
+ transition: ${config.animation ? "all 0.2s ease" : "none"};
40
+ user-select: none;
41
+ }
42
+ .ab-badge__dot {
43
+ width: 6px;
44
+ height: 6px;
45
+ border-radius: 50%;
46
+ background: ${identity.verified ? "#22c55e" : "#94a3b8"};
47
+ flex-shrink: 0;
48
+ }
49
+ .ab-badge__details {
50
+ display: none;
51
+ margin-left: 4px;
52
+ opacity: 0.75;
53
+ white-space: nowrap;
54
+ }
55
+ .ab-badge--expanded .ab-badge__details {
56
+ display: inline;
57
+ }
58
+ </style>
59
+ <div class="ab-badge" part="badge">
60
+ <span class="ab-badge__dot"></span>
61
+ <span>Verified by App Builders PH</span>
62
+ <span class="ab-badge__details">${details}</span>
63
+ </div>
64
+ `;
65
+ }
66
+
67
+ // src/element.ts
68
+ var BADGE_TAG = "appbuilders-badge";
69
+ var registered = false;
70
+ function defineBadgeElement() {
71
+ if (registered) return;
72
+ if (typeof window === "undefined" || typeof HTMLElement === "undefined") return;
73
+ if (customElements.get(BADGE_TAG)) {
74
+ registered = true;
75
+ return;
76
+ }
77
+ class AppBuildersBadgeElement extends HTMLElement {
78
+ shadow;
79
+ constructor() {
80
+ super();
81
+ this.shadow = this.attachShadow({ mode: "open" });
82
+ }
83
+ setConfig(config, identity) {
84
+ this.shadow.innerHTML = template(config, identity);
85
+ if (config.expandable) {
86
+ this.shadow.querySelector(".ab-badge")?.addEventListener("click", (event) => {
87
+ event.currentTarget.classList.toggle("ab-badge--expanded");
88
+ });
89
+ }
90
+ }
91
+ }
92
+ customElements.define(BADGE_TAG, AppBuildersBadgeElement);
93
+ registered = true;
94
+ }
95
+
96
+ // src/types.ts
97
+ var DEFAULT_BADGE_CONFIG = {
98
+ theme: "system",
99
+ position: "bottom-right",
100
+ animation: true,
101
+ expandable: true
102
+ };
103
+ function resolveBadgeConfig(input) {
104
+ if (input === false) return null;
105
+ if (input === void 0 || input === true) return { ...DEFAULT_BADGE_CONFIG };
106
+ if (input.enabled === false) return null;
107
+ return {
108
+ theme: input.theme ?? DEFAULT_BADGE_CONFIG.theme,
109
+ position: input.position ?? DEFAULT_BADGE_CONFIG.position,
110
+ animation: input.animation ?? DEFAULT_BADGE_CONFIG.animation,
111
+ expandable: input.expandable ?? DEFAULT_BADGE_CONFIG.expandable
112
+ };
113
+ }
114
+
115
+ // src/badge.ts
116
+ var Badge = class {
117
+ identity;
118
+ config;
119
+ el = null;
120
+ constructor(identity, config) {
121
+ this.identity = identity;
122
+ this.config = resolveBadgeConfig(config);
123
+ }
124
+ isMounted() {
125
+ return this.el !== null;
126
+ }
127
+ mount(target) {
128
+ if (typeof document === "undefined") return;
129
+ if (this.config === null) return;
130
+ if (this.el) return;
131
+ defineBadgeElement();
132
+ const el = document.createElement(BADGE_TAG);
133
+ el.setConfig(this.config, this.identity);
134
+ (target ?? document.body).appendChild(el);
135
+ this.el = el;
136
+ }
137
+ unmount() {
138
+ if (!this.el) return;
139
+ this.el.remove();
140
+ this.el = null;
141
+ }
142
+ update(config) {
143
+ this.config = resolveBadgeConfig(config);
144
+ if (!this.el) return;
145
+ if (this.config === null) {
146
+ this.unmount();
147
+ return;
148
+ }
149
+ this.el.setConfig(this.config, this.identity);
150
+ }
151
+ };
152
+
153
+ exports.BADGE_TAG = BADGE_TAG;
154
+ exports.Badge = Badge;
155
+ exports.DEFAULT_BADGE_CONFIG = DEFAULT_BADGE_CONFIG;
156
+ exports.defineBadgeElement = defineBadgeElement;
157
+ exports.resolveBadgeConfig = resolveBadgeConfig;
158
+ //# sourceMappingURL=index.cjs.map
159
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/template.ts","../src/element.ts","../src/types.ts","../src/badge.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { BadgeIdentityInfo, ResolvedBadgeConfig } from \"./types.js\";\n\nconst POSITION_STYLES: Record<ResolvedBadgeConfig[\"position\"], string> = {\n \"bottom-right\": \"bottom:16px;right:16px;\",\n \"bottom-left\": \"bottom:16px;left:16px;\",\n \"top-right\": \"top:16px;right:16px;\",\n \"top-left\": \"top:16px;left:16px;\",\n};\n\nfunction resolveTheme(theme: ResolvedBadgeConfig[\"theme\"]): \"dark\" | \"light\" {\n if (theme !== \"system\") return theme;\n if (typeof window !== \"undefined\" && window.matchMedia) {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n }\n return \"dark\";\n}\n\nexport function template(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): string {\n const theme = resolveTheme(config.theme);\n const details = [identity.appName, identity.organization, identity.environment]\n .filter(Boolean)\n .join(\" · \");\n\n return `\n <style>\n :host { all: initial; }\n .ab-badge {\n position: fixed;\n ${POSITION_STYLES[config.position]}\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-radius: 999px;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n line-height: 1.4;\n background: ${theme === \"dark\" ? \"#111\" : \"#fff\"};\n color: ${theme === \"dark\" ? \"#fff\" : \"#111\"};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);\n cursor: ${config.expandable ? \"pointer\" : \"default\"};\n transition: ${config.animation ? \"all 0.2s ease\" : \"none\"};\n user-select: none;\n }\n .ab-badge__dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${identity.verified ? \"#22c55e\" : \"#94a3b8\"};\n flex-shrink: 0;\n }\n .ab-badge__details {\n display: none;\n margin-left: 4px;\n opacity: 0.75;\n white-space: nowrap;\n }\n .ab-badge--expanded .ab-badge__details {\n display: inline;\n }\n </style>\n <div class=\"ab-badge\" part=\"badge\">\n <span class=\"ab-badge__dot\"></span>\n <span>Verified by App Builders PH</span>\n <span class=\"ab-badge__details\">${details}</span>\n </div>\n `;\n}\n","import { template } from \"./template.js\";\nimport type { BadgeIdentityInfo, ResolvedBadgeConfig } from \"./types.js\";\n\nexport const BADGE_TAG = \"appbuilders-badge\";\n\nlet registered = false;\n\nexport function defineBadgeElement(): void {\n if (registered) return;\n if (typeof window === \"undefined\" || typeof HTMLElement === \"undefined\") return;\n if (customElements.get(BADGE_TAG)) {\n registered = true;\n return;\n }\n\n class AppBuildersBadgeElement extends HTMLElement {\n private readonly shadow: ShadowRoot;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void {\n this.shadow.innerHTML = template(config, identity);\n\n if (config.expandable) {\n this.shadow.querySelector(\".ab-badge\")?.addEventListener(\"click\", (event) => {\n (event.currentTarget as HTMLElement).classList.toggle(\"ab-badge--expanded\");\n });\n }\n }\n }\n\n customElements.define(BADGE_TAG, AppBuildersBadgeElement);\n registered = true;\n}\n\nexport interface BadgeElement extends HTMLElement {\n setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void;\n}\n","import type { BadgeConfig, BadgePosition, Environment, Theme } from \"@appbuildersph/shared\";\n\nexport interface BadgeIdentityInfo {\n appName: string;\n organization?: string;\n developer?: string;\n environment?: Environment;\n verified?: boolean;\n}\n\nexport interface ResolvedBadgeConfig {\n theme: Theme;\n position: BadgePosition;\n animation: boolean;\n expandable: boolean;\n}\n\nexport const DEFAULT_BADGE_CONFIG: ResolvedBadgeConfig = {\n theme: \"system\",\n position: \"bottom-right\",\n animation: true,\n expandable: true,\n};\n\nexport function resolveBadgeConfig(input?: boolean | BadgeConfig): ResolvedBadgeConfig | null {\n if (input === false) return null;\n if (input === undefined || input === true) return { ...DEFAULT_BADGE_CONFIG };\n if (input.enabled === false) return null;\n\n return {\n theme: input.theme ?? DEFAULT_BADGE_CONFIG.theme,\n position: input.position ?? DEFAULT_BADGE_CONFIG.position,\n animation: input.animation ?? DEFAULT_BADGE_CONFIG.animation,\n expandable: input.expandable ?? DEFAULT_BADGE_CONFIG.expandable,\n };\n}\n","import type { BadgeConfig } from \"@appbuildersph/shared\";\nimport { BADGE_TAG, defineBadgeElement, type BadgeElement } from \"./element.js\";\nimport { resolveBadgeConfig, type BadgeIdentityInfo, type ResolvedBadgeConfig } from \"./types.js\";\n\nexport class Badge {\n private readonly identity: BadgeIdentityInfo;\n private config: ResolvedBadgeConfig | null;\n private el: BadgeElement | null = null;\n\n constructor(identity: BadgeIdentityInfo, config?: boolean | BadgeConfig) {\n this.identity = identity;\n this.config = resolveBadgeConfig(config);\n }\n\n isMounted(): boolean {\n return this.el !== null;\n }\n\n mount(target?: Element): void {\n if (typeof document === \"undefined\") return;\n if (this.config === null) return;\n if (this.el) return;\n\n defineBadgeElement();\n const el = document.createElement(BADGE_TAG) as BadgeElement;\n el.setConfig(this.config, this.identity);\n (target ?? document.body).appendChild(el);\n this.el = el;\n }\n\n unmount(): void {\n if (!this.el) return;\n this.el.remove();\n this.el = null;\n }\n\n update(config?: boolean | BadgeConfig): void {\n this.config = resolveBadgeConfig(config);\n\n if (!this.el) return;\n if (this.config === null) {\n this.unmount();\n return;\n }\n this.el.setConfig(this.config, this.identity);\n }\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { Environment, Theme, BadgePosition, BadgeConfig } from '@appbuildersph/shared';
2
+
3
+ interface BadgeIdentityInfo {
4
+ appName: string;
5
+ organization?: string;
6
+ developer?: string;
7
+ environment?: Environment;
8
+ verified?: boolean;
9
+ }
10
+ interface ResolvedBadgeConfig {
11
+ theme: Theme;
12
+ position: BadgePosition;
13
+ animation: boolean;
14
+ expandable: boolean;
15
+ }
16
+ declare const DEFAULT_BADGE_CONFIG: ResolvedBadgeConfig;
17
+ declare function resolveBadgeConfig(input?: boolean | BadgeConfig): ResolvedBadgeConfig | null;
18
+
19
+ declare class Badge {
20
+ private readonly identity;
21
+ private config;
22
+ private el;
23
+ constructor(identity: BadgeIdentityInfo, config?: boolean | BadgeConfig);
24
+ isMounted(): boolean;
25
+ mount(target?: Element): void;
26
+ unmount(): void;
27
+ update(config?: boolean | BadgeConfig): void;
28
+ }
29
+
30
+ declare const BADGE_TAG = "appbuilders-badge";
31
+ declare function defineBadgeElement(): void;
32
+ interface BadgeElement extends HTMLElement {
33
+ setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void;
34
+ }
35
+
36
+ export { BADGE_TAG, Badge, type BadgeElement, type BadgeIdentityInfo, DEFAULT_BADGE_CONFIG, type ResolvedBadgeConfig, defineBadgeElement, resolveBadgeConfig };
@@ -0,0 +1,36 @@
1
+ import { Environment, Theme, BadgePosition, BadgeConfig } from '@appbuildersph/shared';
2
+
3
+ interface BadgeIdentityInfo {
4
+ appName: string;
5
+ organization?: string;
6
+ developer?: string;
7
+ environment?: Environment;
8
+ verified?: boolean;
9
+ }
10
+ interface ResolvedBadgeConfig {
11
+ theme: Theme;
12
+ position: BadgePosition;
13
+ animation: boolean;
14
+ expandable: boolean;
15
+ }
16
+ declare const DEFAULT_BADGE_CONFIG: ResolvedBadgeConfig;
17
+ declare function resolveBadgeConfig(input?: boolean | BadgeConfig): ResolvedBadgeConfig | null;
18
+
19
+ declare class Badge {
20
+ private readonly identity;
21
+ private config;
22
+ private el;
23
+ constructor(identity: BadgeIdentityInfo, config?: boolean | BadgeConfig);
24
+ isMounted(): boolean;
25
+ mount(target?: Element): void;
26
+ unmount(): void;
27
+ update(config?: boolean | BadgeConfig): void;
28
+ }
29
+
30
+ declare const BADGE_TAG = "appbuilders-badge";
31
+ declare function defineBadgeElement(): void;
32
+ interface BadgeElement extends HTMLElement {
33
+ setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void;
34
+ }
35
+
36
+ export { BADGE_TAG, Badge, type BadgeElement, type BadgeIdentityInfo, DEFAULT_BADGE_CONFIG, type ResolvedBadgeConfig, defineBadgeElement, resolveBadgeConfig };
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ // src/template.ts
2
+ var POSITION_STYLES = {
3
+ "bottom-right": "bottom:16px;right:16px;",
4
+ "bottom-left": "bottom:16px;left:16px;",
5
+ "top-right": "top:16px;right:16px;",
6
+ "top-left": "top:16px;left:16px;"
7
+ };
8
+ function resolveTheme(theme) {
9
+ if (theme !== "system") return theme;
10
+ if (typeof window !== "undefined" && window.matchMedia) {
11
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
12
+ }
13
+ return "dark";
14
+ }
15
+ function template(config, identity) {
16
+ const theme = resolveTheme(config.theme);
17
+ const details = [identity.appName, identity.organization, identity.environment].filter(Boolean).join(" \xB7 ");
18
+ return `
19
+ <style>
20
+ :host { all: initial; }
21
+ .ab-badge {
22
+ position: fixed;
23
+ ${POSITION_STYLES[config.position]}
24
+ z-index: 2147483647;
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 6px;
28
+ padding: 6px 10px;
29
+ border-radius: 999px;
30
+ font-family: system-ui, -apple-system, sans-serif;
31
+ font-size: 12px;
32
+ line-height: 1.4;
33
+ background: ${theme === "dark" ? "#111" : "#fff"};
34
+ color: ${theme === "dark" ? "#fff" : "#111"};
35
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
36
+ cursor: ${config.expandable ? "pointer" : "default"};
37
+ transition: ${config.animation ? "all 0.2s ease" : "none"};
38
+ user-select: none;
39
+ }
40
+ .ab-badge__dot {
41
+ width: 6px;
42
+ height: 6px;
43
+ border-radius: 50%;
44
+ background: ${identity.verified ? "#22c55e" : "#94a3b8"};
45
+ flex-shrink: 0;
46
+ }
47
+ .ab-badge__details {
48
+ display: none;
49
+ margin-left: 4px;
50
+ opacity: 0.75;
51
+ white-space: nowrap;
52
+ }
53
+ .ab-badge--expanded .ab-badge__details {
54
+ display: inline;
55
+ }
56
+ </style>
57
+ <div class="ab-badge" part="badge">
58
+ <span class="ab-badge__dot"></span>
59
+ <span>Verified by App Builders PH</span>
60
+ <span class="ab-badge__details">${details}</span>
61
+ </div>
62
+ `;
63
+ }
64
+
65
+ // src/element.ts
66
+ var BADGE_TAG = "appbuilders-badge";
67
+ var registered = false;
68
+ function defineBadgeElement() {
69
+ if (registered) return;
70
+ if (typeof window === "undefined" || typeof HTMLElement === "undefined") return;
71
+ if (customElements.get(BADGE_TAG)) {
72
+ registered = true;
73
+ return;
74
+ }
75
+ class AppBuildersBadgeElement extends HTMLElement {
76
+ shadow;
77
+ constructor() {
78
+ super();
79
+ this.shadow = this.attachShadow({ mode: "open" });
80
+ }
81
+ setConfig(config, identity) {
82
+ this.shadow.innerHTML = template(config, identity);
83
+ if (config.expandable) {
84
+ this.shadow.querySelector(".ab-badge")?.addEventListener("click", (event) => {
85
+ event.currentTarget.classList.toggle("ab-badge--expanded");
86
+ });
87
+ }
88
+ }
89
+ }
90
+ customElements.define(BADGE_TAG, AppBuildersBadgeElement);
91
+ registered = true;
92
+ }
93
+
94
+ // src/types.ts
95
+ var DEFAULT_BADGE_CONFIG = {
96
+ theme: "system",
97
+ position: "bottom-right",
98
+ animation: true,
99
+ expandable: true
100
+ };
101
+ function resolveBadgeConfig(input) {
102
+ if (input === false) return null;
103
+ if (input === void 0 || input === true) return { ...DEFAULT_BADGE_CONFIG };
104
+ if (input.enabled === false) return null;
105
+ return {
106
+ theme: input.theme ?? DEFAULT_BADGE_CONFIG.theme,
107
+ position: input.position ?? DEFAULT_BADGE_CONFIG.position,
108
+ animation: input.animation ?? DEFAULT_BADGE_CONFIG.animation,
109
+ expandable: input.expandable ?? DEFAULT_BADGE_CONFIG.expandable
110
+ };
111
+ }
112
+
113
+ // src/badge.ts
114
+ var Badge = class {
115
+ identity;
116
+ config;
117
+ el = null;
118
+ constructor(identity, config) {
119
+ this.identity = identity;
120
+ this.config = resolveBadgeConfig(config);
121
+ }
122
+ isMounted() {
123
+ return this.el !== null;
124
+ }
125
+ mount(target) {
126
+ if (typeof document === "undefined") return;
127
+ if (this.config === null) return;
128
+ if (this.el) return;
129
+ defineBadgeElement();
130
+ const el = document.createElement(BADGE_TAG);
131
+ el.setConfig(this.config, this.identity);
132
+ (target ?? document.body).appendChild(el);
133
+ this.el = el;
134
+ }
135
+ unmount() {
136
+ if (!this.el) return;
137
+ this.el.remove();
138
+ this.el = null;
139
+ }
140
+ update(config) {
141
+ this.config = resolveBadgeConfig(config);
142
+ if (!this.el) return;
143
+ if (this.config === null) {
144
+ this.unmount();
145
+ return;
146
+ }
147
+ this.el.setConfig(this.config, this.identity);
148
+ }
149
+ };
150
+
151
+ export { BADGE_TAG, Badge, DEFAULT_BADGE_CONFIG, defineBadgeElement, resolveBadgeConfig };
152
+ //# sourceMappingURL=index.js.map
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/template.ts","../src/element.ts","../src/types.ts","../src/badge.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { BadgeIdentityInfo, ResolvedBadgeConfig } from \"./types.js\";\n\nconst POSITION_STYLES: Record<ResolvedBadgeConfig[\"position\"], string> = {\n \"bottom-right\": \"bottom:16px;right:16px;\",\n \"bottom-left\": \"bottom:16px;left:16px;\",\n \"top-right\": \"top:16px;right:16px;\",\n \"top-left\": \"top:16px;left:16px;\",\n};\n\nfunction resolveTheme(theme: ResolvedBadgeConfig[\"theme\"]): \"dark\" | \"light\" {\n if (theme !== \"system\") return theme;\n if (typeof window !== \"undefined\" && window.matchMedia) {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n }\n return \"dark\";\n}\n\nexport function template(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): string {\n const theme = resolveTheme(config.theme);\n const details = [identity.appName, identity.organization, identity.environment]\n .filter(Boolean)\n .join(\" · \");\n\n return `\n <style>\n :host { all: initial; }\n .ab-badge {\n position: fixed;\n ${POSITION_STYLES[config.position]}\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-radius: 999px;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n line-height: 1.4;\n background: ${theme === \"dark\" ? \"#111\" : \"#fff\"};\n color: ${theme === \"dark\" ? \"#fff\" : \"#111\"};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);\n cursor: ${config.expandable ? \"pointer\" : \"default\"};\n transition: ${config.animation ? \"all 0.2s ease\" : \"none\"};\n user-select: none;\n }\n .ab-badge__dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${identity.verified ? \"#22c55e\" : \"#94a3b8\"};\n flex-shrink: 0;\n }\n .ab-badge__details {\n display: none;\n margin-left: 4px;\n opacity: 0.75;\n white-space: nowrap;\n }\n .ab-badge--expanded .ab-badge__details {\n display: inline;\n }\n </style>\n <div class=\"ab-badge\" part=\"badge\">\n <span class=\"ab-badge__dot\"></span>\n <span>Verified by App Builders PH</span>\n <span class=\"ab-badge__details\">${details}</span>\n </div>\n `;\n}\n","import { template } from \"./template.js\";\nimport type { BadgeIdentityInfo, ResolvedBadgeConfig } from \"./types.js\";\n\nexport const BADGE_TAG = \"appbuilders-badge\";\n\nlet registered = false;\n\nexport function defineBadgeElement(): void {\n if (registered) return;\n if (typeof window === \"undefined\" || typeof HTMLElement === \"undefined\") return;\n if (customElements.get(BADGE_TAG)) {\n registered = true;\n return;\n }\n\n class AppBuildersBadgeElement extends HTMLElement {\n private readonly shadow: ShadowRoot;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void {\n this.shadow.innerHTML = template(config, identity);\n\n if (config.expandable) {\n this.shadow.querySelector(\".ab-badge\")?.addEventListener(\"click\", (event) => {\n (event.currentTarget as HTMLElement).classList.toggle(\"ab-badge--expanded\");\n });\n }\n }\n }\n\n customElements.define(BADGE_TAG, AppBuildersBadgeElement);\n registered = true;\n}\n\nexport interface BadgeElement extends HTMLElement {\n setConfig(config: ResolvedBadgeConfig, identity: BadgeIdentityInfo): void;\n}\n","import type { BadgeConfig, BadgePosition, Environment, Theme } from \"@appbuildersph/shared\";\n\nexport interface BadgeIdentityInfo {\n appName: string;\n organization?: string;\n developer?: string;\n environment?: Environment;\n verified?: boolean;\n}\n\nexport interface ResolvedBadgeConfig {\n theme: Theme;\n position: BadgePosition;\n animation: boolean;\n expandable: boolean;\n}\n\nexport const DEFAULT_BADGE_CONFIG: ResolvedBadgeConfig = {\n theme: \"system\",\n position: \"bottom-right\",\n animation: true,\n expandable: true,\n};\n\nexport function resolveBadgeConfig(input?: boolean | BadgeConfig): ResolvedBadgeConfig | null {\n if (input === false) return null;\n if (input === undefined || input === true) return { ...DEFAULT_BADGE_CONFIG };\n if (input.enabled === false) return null;\n\n return {\n theme: input.theme ?? DEFAULT_BADGE_CONFIG.theme,\n position: input.position ?? DEFAULT_BADGE_CONFIG.position,\n animation: input.animation ?? DEFAULT_BADGE_CONFIG.animation,\n expandable: input.expandable ?? DEFAULT_BADGE_CONFIG.expandable,\n };\n}\n","import type { BadgeConfig } from \"@appbuildersph/shared\";\nimport { BADGE_TAG, defineBadgeElement, type BadgeElement } from \"./element.js\";\nimport { resolveBadgeConfig, type BadgeIdentityInfo, type ResolvedBadgeConfig } from \"./types.js\";\n\nexport class Badge {\n private readonly identity: BadgeIdentityInfo;\n private config: ResolvedBadgeConfig | null;\n private el: BadgeElement | null = null;\n\n constructor(identity: BadgeIdentityInfo, config?: boolean | BadgeConfig) {\n this.identity = identity;\n this.config = resolveBadgeConfig(config);\n }\n\n isMounted(): boolean {\n return this.el !== null;\n }\n\n mount(target?: Element): void {\n if (typeof document === \"undefined\") return;\n if (this.config === null) return;\n if (this.el) return;\n\n defineBadgeElement();\n const el = document.createElement(BADGE_TAG) as BadgeElement;\n el.setConfig(this.config, this.identity);\n (target ?? document.body).appendChild(el);\n this.el = el;\n }\n\n unmount(): void {\n if (!this.el) return;\n this.el.remove();\n this.el = null;\n }\n\n update(config?: boolean | BadgeConfig): void {\n this.config = resolveBadgeConfig(config);\n\n if (!this.el) return;\n if (this.config === null) {\n this.unmount();\n return;\n }\n this.el.setConfig(this.config, this.identity);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@appbuildersph/badge",
3
+ "version": "0.1.0",
4
+ "description": "Floating Verified Badge UI web component for the App Builders PH SDK.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "dependencies": {
20
+ "@appbuildersph/shared": "0.0.2"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.3.5",
24
+ "typescript": "^5.6.3",
25
+ "vitest": "^2.1.4"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "dev": "tsup --watch",
30
+ "typecheck": "tsc --noEmit",
31
+ "test": "vitest run"
32
+ }
33
+ }