env-flag 1.0.1 → 1.0.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.
- package/dist/index.d.ts +27 -7
- package/dist/index.js +282 -273
- package/package.json +38 -38
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,26 @@
|
|
|
2
2
|
* Environment Flag - A lightweight library to display environment indicators
|
|
3
3
|
* @version 1.0.0
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
declare global {
|
|
6
|
+
interface ImportMeta {
|
|
7
|
+
readonly env: ImportMetaEnv;
|
|
8
|
+
}
|
|
9
|
+
interface ImportMetaEnv {
|
|
10
|
+
readonly NODE_ENV?: string;
|
|
11
|
+
readonly VITE_NODE_ENV?: string;
|
|
12
|
+
readonly VITE_APP_NODE_ENV?: string;
|
|
13
|
+
readonly VITE_APP_ENV?: string;
|
|
14
|
+
readonly VITE_ENV?: string;
|
|
15
|
+
readonly REACT_APP_NODE_ENV?: string;
|
|
16
|
+
readonly REACT_APP_ENV?: string;
|
|
17
|
+
readonly VUE_APP_NODE_ENV?: string;
|
|
18
|
+
readonly VUE_APP_ENV?: string;
|
|
19
|
+
readonly [key: string]: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
type Environment = "development" | "production" | "staging" | "test";
|
|
23
|
+
type Position = "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
24
|
+
type Size = "small" | "medium" | "large";
|
|
8
25
|
interface EnvFlagConfig {
|
|
9
26
|
readonly productionColor?: string;
|
|
10
27
|
readonly developmentColor?: string;
|
|
@@ -32,21 +49,24 @@ declare class EnvFlag {
|
|
|
32
49
|
/**
|
|
33
50
|
* Initialize the environment flag
|
|
34
51
|
*/
|
|
35
|
-
init()
|
|
52
|
+
init: () => void;
|
|
36
53
|
/**
|
|
37
54
|
* Destroy the environment flag and clean up resources
|
|
38
55
|
*/
|
|
39
|
-
destroy()
|
|
56
|
+
destroy: () => void;
|
|
40
57
|
/**
|
|
41
58
|
* Update configuration and recreate flag
|
|
42
59
|
*/
|
|
43
|
-
updateConfig(newConfig: Partial<EnvFlagConfig>)
|
|
60
|
+
updateConfig: (newConfig: Partial<EnvFlagConfig>) => void;
|
|
44
61
|
/**
|
|
45
62
|
* Get current environment
|
|
46
63
|
*/
|
|
47
|
-
getCurrentEnvironment()
|
|
64
|
+
getCurrentEnvironment: () => Environment;
|
|
48
65
|
private isValidEnvironment;
|
|
49
66
|
private detectEnvironment;
|
|
67
|
+
private getNodeEnv;
|
|
68
|
+
private getCustomEnv;
|
|
69
|
+
private isValidEnvironmentString;
|
|
50
70
|
private createFlag;
|
|
51
71
|
private applyStyles;
|
|
52
72
|
private getEnvironmentStyles;
|
package/dist/index.js
CHANGED
|
@@ -6,308 +6,317 @@ class EnvFlag {
|
|
|
6
6
|
constructor(config = {}) {
|
|
7
7
|
this.flagElement = null;
|
|
8
8
|
this.eventListeners = [];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Initialize the environment flag
|
|
16
|
-
*/
|
|
17
|
-
init() {
|
|
18
|
-
try {
|
|
19
|
-
if (!this.isValidEnvironment()) {
|
|
20
|
-
if (this.config.debug) {
|
|
21
|
-
console.warn('[EnvFlag] Invalid environment, skipping initialization');
|
|
22
|
-
}
|
|
9
|
+
/**
|
|
10
|
+
* Initialize the environment flag
|
|
11
|
+
*/
|
|
12
|
+
this.init = () => {
|
|
13
|
+
if (!this.isValidEnvironment())
|
|
23
14
|
return;
|
|
24
|
-
|
|
25
|
-
if (!this.config.enabled) {
|
|
26
|
-
if (this.config.debug) {
|
|
27
|
-
console.log('[EnvFlag] Flag is disabled');
|
|
28
|
-
}
|
|
15
|
+
if (!this.config.enabled)
|
|
29
16
|
return;
|
|
30
|
-
}
|
|
31
17
|
this.destroy(); // Clean up any existing flag
|
|
32
18
|
this.createFlag();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.error('[EnvFlag] Failed to initialize:', error);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Destroy the environment flag and clean up resources
|
|
43
|
-
*/
|
|
44
|
-
destroy() {
|
|
45
|
-
try {
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Destroy the environment flag and clean up resources
|
|
22
|
+
*/
|
|
23
|
+
this.destroy = () => {
|
|
46
24
|
this.removeEventListeners();
|
|
47
25
|
this.removeFlagElement();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Update configuration and recreate flag
|
|
29
|
+
*/
|
|
30
|
+
this.updateConfig = (newConfig) => {
|
|
31
|
+
Object.assign(this.config, newConfig);
|
|
32
|
+
this.init();
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Get current environment
|
|
36
|
+
*/
|
|
37
|
+
this.getCurrentEnvironment = () => {
|
|
38
|
+
if (this.config.forceEnv)
|
|
39
|
+
return this.config.forceEnv;
|
|
40
|
+
if (!this.config.autoDetectEnv)
|
|
41
|
+
return "development";
|
|
42
|
+
return this.detectEnvironment();
|
|
43
|
+
};
|
|
44
|
+
this.isValidEnvironment = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
45
|
+
this.detectEnvironment = () => {
|
|
46
|
+
const indicators = {
|
|
47
|
+
hostname: window.location?.hostname || "",
|
|
48
|
+
nodeEnv: this.getNodeEnv(),
|
|
49
|
+
customEnv: this.getCustomEnv(),
|
|
50
|
+
userAgent: navigator?.userAgent || "",
|
|
51
|
+
};
|
|
52
|
+
// Priority 1: Custom environment variable (highest priority)
|
|
53
|
+
if (indicators.customEnv)
|
|
54
|
+
return indicators.customEnv;
|
|
55
|
+
// Priority 2: NODE_ENV from bundler
|
|
56
|
+
if (indicators.nodeEnv)
|
|
57
|
+
return indicators.nodeEnv;
|
|
58
|
+
// Priority 3: Hostname-based detection
|
|
59
|
+
// Production indicators
|
|
60
|
+
if (indicators.hostname.includes("prod") ||
|
|
61
|
+
(!indicators.hostname.includes("localhost") &&
|
|
62
|
+
!indicators.hostname.includes("127.0.0.1") &&
|
|
63
|
+
!indicators.hostname.includes("dev") &&
|
|
64
|
+
!indicators.hostname.includes("staging") &&
|
|
65
|
+
!indicators.hostname.includes("test") &&
|
|
66
|
+
indicators.hostname.length > 0))
|
|
67
|
+
return "production";
|
|
68
|
+
// Staging indicators
|
|
69
|
+
if (indicators.hostname.includes("staging") ||
|
|
70
|
+
indicators.hostname.includes("stage"))
|
|
71
|
+
return "staging";
|
|
72
|
+
// Test indicators
|
|
73
|
+
if (indicators.hostname.includes("test"))
|
|
74
|
+
return "test";
|
|
75
|
+
return "development";
|
|
76
|
+
};
|
|
77
|
+
this.getNodeEnv = () => {
|
|
78
|
+
// Vite environment (import.meta.env)
|
|
79
|
+
if (typeof import.meta !== "undefined" && import.meta.env) {
|
|
80
|
+
const viteEnv = import.meta.env.NODE_ENV || import.meta.env.VITE_NODE_ENV;
|
|
81
|
+
if (viteEnv && this.isValidEnvironmentString(viteEnv)) {
|
|
82
|
+
return viteEnv;
|
|
83
|
+
}
|
|
79
84
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
// Webpack/traditional bundler environment (process.env)
|
|
86
|
+
if (typeof globalThis !== "undefined" &&
|
|
87
|
+
globalThis.process?.env?.NODE_ENV) {
|
|
88
|
+
const nodeEnv = globalThis.process.env.NODE_ENV;
|
|
89
|
+
if (this.isValidEnvironmentString(nodeEnv))
|
|
90
|
+
return nodeEnv;
|
|
85
91
|
}
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
detectEnvironment() {
|
|
91
|
-
var _a, _b, _c;
|
|
92
|
-
// Check various environment indicators
|
|
93
|
-
const indicators = {
|
|
94
|
-
hostname: ((_a = window.location) === null || _a === void 0 ? void 0 : _a.hostname) || '',
|
|
95
|
-
nodeEnv: ((_c = (_b = globalThis.process) === null || _b === void 0 ? void 0 : _b.env) === null || _c === void 0 ? void 0 : _c.NODE_ENV) || '',
|
|
96
|
-
userAgent: (navigator === null || navigator === void 0 ? void 0 : navigator.userAgent) || ''
|
|
92
|
+
return "";
|
|
97
93
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return 'test';
|
|
120
|
-
}
|
|
121
|
-
// Default to development
|
|
122
|
-
return 'development';
|
|
123
|
-
}
|
|
124
|
-
createFlag() {
|
|
125
|
-
const environment = this.getCurrentEnvironment();
|
|
126
|
-
this.flagElement = document.createElement('div');
|
|
127
|
-
this.flagElement.id = EnvFlag.ELEMENT_ID;
|
|
128
|
-
this.flagElement.setAttribute('data-env', environment);
|
|
129
|
-
this.flagElement.setAttribute('role', 'status');
|
|
130
|
-
this.flagElement.setAttribute('aria-label', `Environment: ${environment}`);
|
|
131
|
-
this.applyStyles(environment);
|
|
132
|
-
this.attachEventListeners();
|
|
133
|
-
// Use requestAnimationFrame for better performance
|
|
134
|
-
requestAnimationFrame(() => {
|
|
135
|
-
if (this.flagElement && document.body) {
|
|
136
|
-
document.body.appendChild(this.flagElement);
|
|
94
|
+
this.getCustomEnv = () => {
|
|
95
|
+
const customEnvVars = [
|
|
96
|
+
"VITE_APP_NODE_ENV",
|
|
97
|
+
"VITE_APP_ENV",
|
|
98
|
+
"VITE_ENV",
|
|
99
|
+
"REACT_APP_NODE_ENV",
|
|
100
|
+
"REACT_APP_ENV",
|
|
101
|
+
"VUE_APP_NODE_ENV",
|
|
102
|
+
"VUE_APP_ENV",
|
|
103
|
+
];
|
|
104
|
+
// Vite environment
|
|
105
|
+
if (typeof import.meta !== "undefined" && import.meta.env) {
|
|
106
|
+
for (const envVar of customEnvVars) {
|
|
107
|
+
const value = import.meta.env[envVar];
|
|
108
|
+
if (value && this.isValidEnvironmentString(value)) {
|
|
109
|
+
if (this.config.debug) {
|
|
110
|
+
console.log(`[EnvFlag] Found custom env variable ${envVar}:`, value);
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
137
115
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
backgroundColor: styles.backgroundColor, color: styles.color,
|
|
152
|
-
// Typography
|
|
153
|
-
fontFamily: '"Segoe UI", system-ui, -apple-system, sans-serif', fontWeight: '600', fontSize: sizeStyles.fontSize, lineHeight: '1', textAlign: 'center',
|
|
154
|
-
// Layout
|
|
155
|
-
position: 'fixed', zIndex: EnvFlag.Z_INDEX, padding: sizeStyles.padding,
|
|
156
|
-
// Visual
|
|
157
|
-
borderRadius: positionStyles.borderRadius, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)', backdropFilter: 'blur(8px)', opacity: styles.opacity,
|
|
158
|
-
// Interaction
|
|
159
|
-
cursor: 'pointer', userSelect: 'none', transition: 'all 0.2s ease-in-out' }, this.getPositionCoordinates()), {
|
|
160
|
-
// Accessibility
|
|
161
|
-
outline: 'none' }));
|
|
162
|
-
this.flagElement.textContent = this.getEnvironmentText(environment);
|
|
163
|
-
}
|
|
164
|
-
getEnvironmentStyles(environment) {
|
|
165
|
-
const colorMap = {
|
|
166
|
-
production: this.config.productionColor,
|
|
167
|
-
development: this.config.developmentColor,
|
|
168
|
-
staging: this.config.stagingColor,
|
|
169
|
-
test: this.config.testColor
|
|
116
|
+
// Webpack/traditional bundler environment
|
|
117
|
+
if (typeof globalThis !== "undefined" && globalThis.process?.env) {
|
|
118
|
+
for (const envVar of customEnvVars) {
|
|
119
|
+
const value = globalThis.process.env[envVar];
|
|
120
|
+
if (value && this.isValidEnvironmentString(value)) {
|
|
121
|
+
if (this.config.debug) {
|
|
122
|
+
console.log(`[EnvFlag] Found custom env variable ${envVar}:`, value);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return "";
|
|
170
129
|
};
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
130
|
+
this.isValidEnvironmentString = (env) => ["development", "production", "staging", "test"].includes(env);
|
|
131
|
+
this.createFlag = () => {
|
|
132
|
+
const environment = this.getCurrentEnvironment();
|
|
133
|
+
this.flagElement = document.createElement("div");
|
|
134
|
+
this.flagElement.id = EnvFlag.ELEMENT_ID;
|
|
135
|
+
this.flagElement.setAttribute("data-env", environment);
|
|
136
|
+
this.flagElement.setAttribute("role", "status");
|
|
137
|
+
this.flagElement.setAttribute("aria-label", `Environment: ${environment}`);
|
|
138
|
+
this.applyStyles(environment);
|
|
139
|
+
this.attachEventListeners();
|
|
140
|
+
// Use requestAnimationFrame for better performance
|
|
141
|
+
requestAnimationFrame(() => {
|
|
142
|
+
if (this.flagElement && document.body) {
|
|
143
|
+
document.body.appendChild(this.flagElement);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
178
146
|
};
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
147
|
+
this.applyStyles = (environment) => {
|
|
148
|
+
if (!this.flagElement)
|
|
149
|
+
return;
|
|
150
|
+
const styles = this.getEnvironmentStyles(environment);
|
|
151
|
+
const positionStyles = this.getPositionStyles();
|
|
152
|
+
const sizeStyles = this.getSizeStyles();
|
|
153
|
+
// Apply base styles
|
|
154
|
+
Object.assign(this.flagElement.style, {
|
|
155
|
+
// Content
|
|
156
|
+
textContent: this.getEnvironmentText(environment),
|
|
157
|
+
// Colors
|
|
158
|
+
backgroundColor: styles.backgroundColor,
|
|
159
|
+
color: styles.color,
|
|
160
|
+
// Typography
|
|
161
|
+
fontFamily: '"Segoe UI", system-ui, -apple-system, sans-serif',
|
|
162
|
+
fontWeight: "600",
|
|
163
|
+
fontSize: sizeStyles.fontSize,
|
|
164
|
+
lineHeight: "1",
|
|
165
|
+
textAlign: "center",
|
|
166
|
+
// Layout
|
|
167
|
+
position: "fixed",
|
|
168
|
+
zIndex: EnvFlag.Z_INDEX,
|
|
169
|
+
padding: sizeStyles.padding,
|
|
170
|
+
// Visual
|
|
171
|
+
borderRadius: positionStyles.borderRadius,
|
|
172
|
+
opacity: styles.opacity,
|
|
173
|
+
// Interaction
|
|
174
|
+
cursor: "pointer",
|
|
175
|
+
userSelect: "none",
|
|
176
|
+
transition: "all 0.2s ease-in-out",
|
|
177
|
+
// Positioning
|
|
178
|
+
...this.getPositionCoordinates(),
|
|
179
|
+
// Accessibility
|
|
180
|
+
outline: "none",
|
|
181
|
+
});
|
|
182
|
+
this.flagElement.textContent = this.getEnvironmentText(environment);
|
|
183
|
+
};
|
|
184
|
+
this.getEnvironmentStyles = (environment) => {
|
|
185
|
+
const colorMap = {
|
|
186
|
+
production: this.config.productionColor,
|
|
187
|
+
development: this.config.developmentColor,
|
|
188
|
+
staging: this.config.stagingColor,
|
|
189
|
+
test: this.config.testColor,
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
backgroundColor: colorMap[environment],
|
|
193
|
+
color: "#ffffff",
|
|
194
|
+
fontSize: this.getSizeStyles().fontSize,
|
|
195
|
+
padding: this.getSizeStyles().padding,
|
|
196
|
+
borderRadius: this.getPositionStyles().borderRadius,
|
|
197
|
+
opacity: "0.9",
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
this.getEnvironmentText = (environment) => ({
|
|
182
201
|
production: this.config.productionText,
|
|
183
202
|
development: this.config.developmentText,
|
|
184
203
|
staging: this.config.stagingText,
|
|
185
|
-
test: this.config.testText
|
|
204
|
+
test: this.config.testText,
|
|
205
|
+
}[environment]);
|
|
206
|
+
this.getSizeStyles = () => ({
|
|
207
|
+
small: { fontSize: "10px", padding: "4px 8px" },
|
|
208
|
+
medium: { fontSize: "12px", padding: "6px 12px" },
|
|
209
|
+
large: { fontSize: "14px", padding: "8px 16px" },
|
|
210
|
+
}[this.config.size]);
|
|
211
|
+
this.getPositionStyles = () => ({
|
|
212
|
+
borderRadius: {
|
|
213
|
+
"top-right": "0 0 0 4px",
|
|
214
|
+
"top-left": "0 0 4px 0",
|
|
215
|
+
"bottom-left": "0 4px 0 0",
|
|
216
|
+
"bottom-right": "4px 0 0 0",
|
|
217
|
+
}[this.config.position],
|
|
218
|
+
});
|
|
219
|
+
this.getPositionCoordinates = () => ({
|
|
220
|
+
"top-right": { top: "0", right: "0" },
|
|
221
|
+
"top-left": { top: "0", left: "0" },
|
|
222
|
+
"bottom-left": { bottom: "0", left: "0" },
|
|
223
|
+
"bottom-right": { bottom: "0", right: "0" },
|
|
224
|
+
}[this.config.position]);
|
|
225
|
+
this.attachEventListeners = () => {
|
|
226
|
+
if (!this.flagElement)
|
|
227
|
+
return;
|
|
228
|
+
// Click to remove
|
|
229
|
+
const clickHandler = this.handleClick.bind(this);
|
|
230
|
+
this.flagElement.addEventListener("click", clickHandler);
|
|
231
|
+
this.eventListeners.push({
|
|
232
|
+
element: this.flagElement,
|
|
233
|
+
event: "click",
|
|
234
|
+
handler: clickHandler,
|
|
235
|
+
});
|
|
236
|
+
// Hover effects
|
|
237
|
+
const mouseEnterHandler = this.handleMouseEnter.bind(this);
|
|
238
|
+
const mouseLeaveHandler = this.handleMouseLeave.bind(this);
|
|
239
|
+
this.flagElement.addEventListener("mouseenter", mouseEnterHandler);
|
|
240
|
+
this.flagElement.addEventListener("mouseleave", mouseLeaveHandler);
|
|
241
|
+
this.eventListeners.push({
|
|
242
|
+
element: this.flagElement,
|
|
243
|
+
event: "mouseenter",
|
|
244
|
+
handler: mouseEnterHandler,
|
|
245
|
+
}, {
|
|
246
|
+
element: this.flagElement,
|
|
247
|
+
event: "mouseleave",
|
|
248
|
+
handler: mouseLeaveHandler,
|
|
249
|
+
});
|
|
250
|
+
// Keyboard accessibility
|
|
251
|
+
const keydownHandler = this.handleKeydown.bind(this);
|
|
252
|
+
this.flagElement.addEventListener("keydown", keydownHandler);
|
|
253
|
+
this.flagElement.setAttribute("tabindex", "0");
|
|
254
|
+
this.eventListeners.push({
|
|
255
|
+
element: this.flagElement,
|
|
256
|
+
event: "keydown",
|
|
257
|
+
handler: keydownHandler,
|
|
258
|
+
});
|
|
186
259
|
};
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
large: { fontSize: '14px', padding: '8px 16px' }
|
|
260
|
+
this.handleClick = () => this.destroy();
|
|
261
|
+
this.handleMouseEnter = () => {
|
|
262
|
+
if (this.flagElement) {
|
|
263
|
+
this.flagElement.style.opacity = "1";
|
|
264
|
+
this.flagElement.style.transform = "scale(1.05)";
|
|
265
|
+
}
|
|
194
266
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
'top-left': '0 0 4px 0',
|
|
201
|
-
'bottom-left': '0 4px 0 0',
|
|
202
|
-
'bottom-right': '4px 0 0 0'
|
|
267
|
+
this.handleMouseLeave = () => {
|
|
268
|
+
if (this.flagElement) {
|
|
269
|
+
this.flagElement.style.opacity = "0.9";
|
|
270
|
+
this.flagElement.style.transform = "scale(1)";
|
|
271
|
+
}
|
|
203
272
|
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
'bottom-left': { bottom: '0', left: '0' },
|
|
211
|
-
'bottom-right': { bottom: '0', right: '0' }
|
|
273
|
+
this.handleKeydown = (event) => {
|
|
274
|
+
const keyboardEvent = event;
|
|
275
|
+
if (keyboardEvent.key === "Enter" || keyboardEvent.key === " ") {
|
|
276
|
+
keyboardEvent.preventDefault();
|
|
277
|
+
this.destroy();
|
|
278
|
+
}
|
|
212
279
|
};
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
// Hover effects
|
|
227
|
-
const mouseEnterHandler = this.handleMouseEnter.bind(this);
|
|
228
|
-
const mouseLeaveHandler = this.handleMouseLeave.bind(this);
|
|
229
|
-
this.flagElement.addEventListener('mouseenter', mouseEnterHandler);
|
|
230
|
-
this.flagElement.addEventListener('mouseleave', mouseLeaveHandler);
|
|
231
|
-
this.eventListeners.push({
|
|
232
|
-
element: this.flagElement,
|
|
233
|
-
event: 'mouseenter',
|
|
234
|
-
handler: mouseEnterHandler
|
|
235
|
-
}, {
|
|
236
|
-
element: this.flagElement,
|
|
237
|
-
event: 'mouseleave',
|
|
238
|
-
handler: mouseLeaveHandler
|
|
239
|
-
});
|
|
240
|
-
// Keyboard accessibility
|
|
241
|
-
const keydownHandler = this.handleKeydown.bind(this);
|
|
242
|
-
this.flagElement.addEventListener('keydown', keydownHandler);
|
|
243
|
-
this.flagElement.setAttribute('tabindex', '0');
|
|
244
|
-
this.eventListeners.push({
|
|
245
|
-
element: this.flagElement,
|
|
246
|
-
event: 'keydown',
|
|
247
|
-
handler: keydownHandler
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
handleClick() {
|
|
251
|
-
this.destroy();
|
|
252
|
-
}
|
|
253
|
-
handleMouseEnter() {
|
|
254
|
-
if (this.flagElement) {
|
|
255
|
-
this.flagElement.style.opacity = '1';
|
|
256
|
-
this.flagElement.style.transform = 'scale(1.05)';
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
handleMouseLeave() {
|
|
260
|
-
if (this.flagElement) {
|
|
261
|
-
this.flagElement.style.opacity = '0.9';
|
|
262
|
-
this.flagElement.style.transform = 'scale(1)';
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
handleKeydown(event) {
|
|
266
|
-
const keyboardEvent = event;
|
|
267
|
-
if (keyboardEvent.key === 'Enter' || keyboardEvent.key === ' ') {
|
|
268
|
-
keyboardEvent.preventDefault();
|
|
269
|
-
this.destroy();
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
removeEventListeners() {
|
|
273
|
-
this.eventListeners.forEach(({ element, event, handler }) => {
|
|
274
|
-
element.removeEventListener(event, handler);
|
|
275
|
-
});
|
|
276
|
-
this.eventListeners = [];
|
|
277
|
-
}
|
|
278
|
-
removeFlagElement() {
|
|
279
|
-
var _a;
|
|
280
|
-
if (this.flagElement && ((_a = document.body) === null || _a === void 0 ? void 0 : _a.contains(this.flagElement))) {
|
|
281
|
-
document.body.removeChild(this.flagElement);
|
|
282
|
-
this.flagElement = null;
|
|
283
|
-
}
|
|
280
|
+
this.removeEventListeners = () => {
|
|
281
|
+
this.eventListeners.forEach(({ element, event, handler }) => {
|
|
282
|
+
element.removeEventListener(event, handler);
|
|
283
|
+
});
|
|
284
|
+
this.eventListeners = [];
|
|
285
|
+
};
|
|
286
|
+
this.removeFlagElement = () => {
|
|
287
|
+
if (this.flagElement && document.body?.contains(this.flagElement)) {
|
|
288
|
+
document.body.removeChild(this.flagElement);
|
|
289
|
+
this.flagElement = null;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
this.config = { ...EnvFlag.DEFAULT_CONFIG, ...config };
|
|
284
293
|
}
|
|
285
294
|
}
|
|
286
295
|
EnvFlag.DEFAULT_CONFIG = {
|
|
287
|
-
productionColor:
|
|
288
|
-
developmentColor:
|
|
289
|
-
stagingColor:
|
|
290
|
-
testColor:
|
|
291
|
-
productionText:
|
|
292
|
-
developmentText:
|
|
293
|
-
stagingText:
|
|
294
|
-
testText:
|
|
295
|
-
position:
|
|
296
|
-
size:
|
|
296
|
+
productionColor: "#f8285a",
|
|
297
|
+
developmentColor: "#17c653",
|
|
298
|
+
stagingColor: "#f39c12",
|
|
299
|
+
testColor: "#9b59b6",
|
|
300
|
+
productionText: "PROD",
|
|
301
|
+
developmentText: "DEV",
|
|
302
|
+
stagingText: "STAGING",
|
|
303
|
+
testText: "TEST",
|
|
304
|
+
position: "bottom-right",
|
|
305
|
+
size: "medium",
|
|
297
306
|
autoDetectEnv: true,
|
|
298
307
|
enabled: true,
|
|
299
|
-
debug: false
|
|
308
|
+
debug: false,
|
|
300
309
|
};
|
|
301
|
-
EnvFlag.ELEMENT_ID =
|
|
302
|
-
EnvFlag.Z_INDEX =
|
|
310
|
+
EnvFlag.ELEMENT_ID = "env-flag-indicator";
|
|
311
|
+
EnvFlag.Z_INDEX = "999999";
|
|
303
312
|
// Auto-initialization for immediate testing
|
|
304
|
-
if (typeof window !==
|
|
313
|
+
if (typeof window !== "undefined") {
|
|
305
314
|
const autoEnvFlag = new EnvFlag({
|
|
306
|
-
debug: true // Enable debug mode for testing
|
|
315
|
+
debug: true, // Enable debug mode for testing
|
|
307
316
|
});
|
|
308
317
|
// Initialize when DOM is ready
|
|
309
|
-
if (document.readyState ===
|
|
310
|
-
document.addEventListener(
|
|
318
|
+
if (document.readyState === "loading") {
|
|
319
|
+
document.addEventListener("DOMContentLoaded", () => autoEnvFlag.init());
|
|
311
320
|
}
|
|
312
321
|
else {
|
|
313
322
|
// DOM is already ready
|
package/package.json
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "env-flag",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"module": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist",
|
|
10
|
-
"README.md"
|
|
11
|
-
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "tsc",
|
|
14
|
-
"start": "tsc && node dist/index.js",
|
|
15
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"env",
|
|
19
|
-
"flag",
|
|
20
|
-
"environment",
|
|
21
|
-
"indicator",
|
|
22
|
-
"typescript",
|
|
23
|
-
"browser",
|
|
24
|
-
"badge"
|
|
25
|
-
],
|
|
26
|
-
"author": "Koray TUNCER <ktuncerr@gmail.com>",
|
|
27
|
-
"license": "ISC",
|
|
28
|
-
"description": "A lightweight, customizable environment flag indicator for browser apps. Shows DEV/PROD/STAGING status as a badge.",
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "https://github.com/koraytuncer/env-flag.git"
|
|
32
|
-
},
|
|
33
|
-
"homepage": "https://github.com/koraytuncer/env-flag",
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@types/node": "^24.0.11",
|
|
36
|
-
"typescript": "^5.8.3"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "env-flag",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "tsc && node dist/index.js",
|
|
15
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"env",
|
|
19
|
+
"flag",
|
|
20
|
+
"environment",
|
|
21
|
+
"indicator",
|
|
22
|
+
"typescript",
|
|
23
|
+
"browser",
|
|
24
|
+
"badge"
|
|
25
|
+
],
|
|
26
|
+
"author": "Koray TUNCER <ktuncerr@gmail.com>",
|
|
27
|
+
"license": "ISC",
|
|
28
|
+
"description": "A lightweight, customizable environment flag indicator for browser apps. Shows DEV/PROD/STAGING status as a badge.",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/koraytuncer/env-flag.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/koraytuncer/env-flag",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^24.0.11",
|
|
36
|
+
"typescript": "^5.8.3"
|
|
37
|
+
}
|
|
38
|
+
}
|