@localflag/react 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 localflag - Daniel Gietmann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # @localflag/react
2
+
3
+ Type-safe feature flags for React with built-in DevTools.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @localflag/react
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { FeatureFlagProvider, useFeatureFlag, DevTools } from '@localflag/react';
15
+
16
+ // 1. Define your flags
17
+ const defaultFlags = {
18
+ darkMode: false,
19
+ newCheckout: true,
20
+ maxItems: 10,
21
+ } as const;
22
+
23
+ // 2. Create a type for your flags
24
+ type AppFlags = typeof defaultFlags;
25
+
26
+ // 3. Wrap your app with the provider
27
+ function App() {
28
+ return (
29
+ <FeatureFlagProvider<AppFlags> defaultFlags={defaultFlags}>
30
+ <MyApp />
31
+ <DevTools /> {/* Optional: adds a floating panel to toggle flags */}
32
+ </FeatureFlagProvider>
33
+ );
34
+ }
35
+
36
+ // 4. Use flags in your components
37
+ function MyComponent() {
38
+ const isDarkMode = useFeatureFlag<AppFlags>('darkMode');
39
+
40
+ return <div className={isDarkMode ? 'dark' : 'light'}>...</div>;
41
+ }
42
+ ```
43
+
44
+ ## API
45
+
46
+ ### `FeatureFlagProvider`
47
+
48
+ The context provider that makes feature flags available to your app.
49
+
50
+ ```tsx
51
+ <FeatureFlagProvider<AppFlags>
52
+ defaultFlags={defaultFlags}
53
+ storageKey="my-app-flags" // Optional: localStorage key (default: 'feature-flags')
54
+ persistOverrides={true} // Optional: persist overrides to localStorage (default: true)
55
+ >
56
+ {children}
57
+ </FeatureFlagProvider>
58
+ ```
59
+
60
+ ### Hooks
61
+
62
+ #### `useFeatureFlag<T>(flagName)`
63
+
64
+ Returns `true` if the flag is enabled (truthy), `false` otherwise.
65
+
66
+ ```tsx
67
+ const isEnabled = useFeatureFlag<AppFlags>('darkMode');
68
+ // Returns: boolean
69
+ ```
70
+
71
+ #### `useFeatureFlagValue<T>(flagName)`
72
+
73
+ Returns the actual value of the flag.
74
+
75
+ ```tsx
76
+ const maxItems = useFeatureFlagValue<AppFlags>('maxItems');
77
+ // Returns: 10 (the actual value)
78
+ ```
79
+
80
+ #### `useFeatureFlags<T>()`
81
+
82
+ Returns all current flag values.
83
+
84
+ ```tsx
85
+ const flags = useFeatureFlags<AppFlags>();
86
+ // Returns: { darkMode: false, newCheckout: true, maxItems: 10 }
87
+ ```
88
+
89
+ #### `useFeatureFlagControls<T>()`
90
+
91
+ Returns functions to control flags programmatically.
92
+
93
+ ```tsx
94
+ const { setFlag, resetFlags } = useFeatureFlagControls<AppFlags>();
95
+
96
+ // Set a specific flag
97
+ setFlag('darkMode', true);
98
+
99
+ // Reset all flags to defaults
100
+ resetFlags();
101
+ ```
102
+
103
+ ### Components
104
+
105
+ #### `FeatureFlag`
106
+
107
+ Conditionally render content based on a flag.
108
+
109
+ ```tsx
110
+ import { FeatureFlag } from '@localflag/react';
111
+
112
+ <FeatureFlag<AppFlags> flag="newCheckout">
113
+ <NewCheckoutFlow />
114
+ </FeatureFlag>
115
+
116
+ // With fallback
117
+ <FeatureFlag<AppFlags> flag="newCheckout" fallback={<OldCheckout />}>
118
+ <NewCheckoutFlow />
119
+ </FeatureFlag>
120
+ ```
121
+
122
+ #### `DevTools`
123
+
124
+ A floating panel for toggling flags during development.
125
+
126
+ ```tsx
127
+ import { DevTools } from '@localflag/react';
128
+
129
+ <DevTools
130
+ position="bottom-right" // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
131
+ defaultOpen={false} // Start expanded or collapsed
132
+ />
133
+ ```
134
+
135
+ ## TypeScript
136
+
137
+ The library is fully typed. Define your flags with `as const` to get full type inference:
138
+
139
+ ```tsx
140
+ const defaultFlags = {
141
+ darkMode: false,
142
+ newFeature: true,
143
+ apiVersion: 'v2',
144
+ maxRetries: 3,
145
+ } as const;
146
+
147
+ type AppFlags = typeof defaultFlags;
148
+
149
+ // Now all hooks will autocomplete flag names and infer value types
150
+ const version = useFeatureFlagValue<AppFlags>('apiVersion');
151
+ // TypeScript knows this is 'v2' (string literal type)
152
+ ```
153
+
154
+ ## Persistence
155
+
156
+ By default, flag overrides are persisted to `localStorage`. This means:
157
+
158
+ - Flags you toggle in DevTools persist across page reloads
159
+ - Each user can have their own overrides for testing
160
+ - Call `resetFlags()` to clear all overrides and return to defaults
161
+
162
+ To disable persistence:
163
+
164
+ ```tsx
165
+ <FeatureFlagProvider defaultFlags={defaultFlags} persistOverrides={false}>
166
+ ```
167
+
168
+ ## License
169
+
170
+ MIT
@@ -0,0 +1,58 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type FlagValue = boolean | string | number;
4
+ interface FeatureFlags {
5
+ [key: string]: FlagValue;
6
+ }
7
+ interface FeatureFlagContextValue<T extends FeatureFlags = FeatureFlags> {
8
+ flags: T;
9
+ isEnabled: (flagName: keyof T) => boolean;
10
+ getValue: <K extends keyof T>(flagName: K) => T[K];
11
+ setFlag: <K extends keyof T>(flagName: K, value: T[K]) => void;
12
+ resetFlags: () => void;
13
+ }
14
+ interface FeatureFlagProviderProps<T extends FeatureFlags = FeatureFlags> {
15
+ children: React.ReactNode;
16
+ defaultFlags: T;
17
+ storageKey?: string;
18
+ persistOverrides?: boolean;
19
+ }
20
+
21
+ declare function FeatureFlagProvider<T extends FeatureFlags>({ children, defaultFlags, storageKey, persistOverrides, }: FeatureFlagProviderProps<T>): react_jsx_runtime.JSX.Element;
22
+ declare function useFeatureFlagContext<T extends FeatureFlags = FeatureFlags>(): FeatureFlagContextValue<T>;
23
+
24
+ /**
25
+ * Hook to check if a feature flag is enabled (truthy)
26
+ */
27
+ declare function useFeatureFlag<T extends FeatureFlags = FeatureFlags>(flagName: keyof T): boolean;
28
+ /**
29
+ * Hook to get the raw value of a feature flag
30
+ */
31
+ declare function useFeatureFlagValue<T extends FeatureFlags = FeatureFlags, K extends keyof T = keyof T>(flagName: K): T[K];
32
+ /**
33
+ * Hook to get all feature flags
34
+ */
35
+ declare function useFeatureFlags<T extends FeatureFlags = FeatureFlags>(): T;
36
+ /**
37
+ * Hook to get flag controls (setFlag, resetFlags)
38
+ */
39
+ declare function useFeatureFlagControls<T extends FeatureFlags = FeatureFlags>(): {
40
+ setFlag: <K extends keyof T>(flagName: K, value: T[K]) => void;
41
+ resetFlags: () => void;
42
+ };
43
+ /**
44
+ * Component wrapper that renders children only if flag is enabled
45
+ */
46
+ declare function FeatureFlag<T extends FeatureFlags = FeatureFlags>({ flag, children, fallback, }: {
47
+ flag: keyof T;
48
+ children: React.ReactNode;
49
+ fallback?: React.ReactNode;
50
+ }): React.ReactNode;
51
+
52
+ interface DevToolsProps {
53
+ position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
54
+ defaultOpen?: boolean;
55
+ }
56
+ declare function DevTools({ position, defaultOpen, }: DevToolsProps): react_jsx_runtime.JSX.Element | null;
57
+
58
+ export { DevTools, FeatureFlag, type FeatureFlagContextValue, FeatureFlagProvider, type FeatureFlagProviderProps, type FeatureFlags, type FlagValue, useFeatureFlag, useFeatureFlagContext, useFeatureFlagControls, useFeatureFlagValue, useFeatureFlags };
package/dist/index.js ADDED
@@ -0,0 +1,467 @@
1
+ // src/context.tsx
2
+ import {
3
+ createContext,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useMemo,
8
+ useState,
9
+ useSyncExternalStore
10
+ } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var FeatureFlagContext = createContext(null);
13
+ var STORAGE_KEY_DEFAULT = "localflag:overrides";
14
+ function getStoredOverrides(storageKey) {
15
+ if (typeof window === "undefined") return {};
16
+ try {
17
+ const stored = localStorage.getItem(storageKey);
18
+ return stored ? JSON.parse(stored) : {};
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+ function setStoredOverrides(storageKey, overrides) {
24
+ if (typeof window === "undefined") return;
25
+ try {
26
+ if (Object.keys(overrides).length === 0) {
27
+ localStorage.removeItem(storageKey);
28
+ } else {
29
+ localStorage.setItem(storageKey, JSON.stringify(overrides));
30
+ }
31
+ } catch {
32
+ }
33
+ }
34
+ var listeners = /* @__PURE__ */ new Set();
35
+ var currentState = null;
36
+ function subscribeToFlagChanges(listener) {
37
+ listeners.add(listener);
38
+ return () => listeners.delete(listener);
39
+ }
40
+ function getFlagState() {
41
+ return currentState;
42
+ }
43
+ function notifyListeners() {
44
+ listeners.forEach((listener) => listener());
45
+ }
46
+ function FeatureFlagProvider({
47
+ children,
48
+ defaultFlags,
49
+ storageKey = STORAGE_KEY_DEFAULT,
50
+ persistOverrides = true
51
+ }) {
52
+ const [overrides, setOverrides] = useState(
53
+ () => persistOverrides ? getStoredOverrides(storageKey) : {}
54
+ );
55
+ const flags = useMemo(
56
+ () => ({ ...defaultFlags, ...overrides }),
57
+ [defaultFlags, overrides]
58
+ );
59
+ useEffect(() => {
60
+ currentState = { flags, defaultFlags };
61
+ notifyListeners();
62
+ }, [flags, defaultFlags]);
63
+ useEffect(() => {
64
+ if (persistOverrides) {
65
+ setStoredOverrides(storageKey, overrides);
66
+ }
67
+ }, [overrides, storageKey, persistOverrides]);
68
+ const isEnabled = useCallback(
69
+ (flagName) => {
70
+ const value2 = flags[flagName];
71
+ return Boolean(value2);
72
+ },
73
+ [flags]
74
+ );
75
+ const getValue = useCallback(
76
+ (flagName) => {
77
+ return flags[flagName];
78
+ },
79
+ [flags]
80
+ );
81
+ const setFlag = useCallback((flagName, value2) => {
82
+ setOverrides((prev) => ({ ...prev, [flagName]: value2 }));
83
+ }, []);
84
+ const resetFlags = useCallback(() => {
85
+ setOverrides({});
86
+ }, []);
87
+ const value = useMemo(
88
+ () => ({
89
+ flags,
90
+ isEnabled,
91
+ getValue,
92
+ setFlag,
93
+ resetFlags
94
+ }),
95
+ [flags, isEnabled, getValue, setFlag, resetFlags]
96
+ );
97
+ return /* @__PURE__ */ jsx(FeatureFlagContext.Provider, { value, children });
98
+ }
99
+ function useFeatureFlagContext() {
100
+ const context = useContext(FeatureFlagContext);
101
+ if (!context) {
102
+ throw new Error(
103
+ "useFeatureFlagContext must be used within a FeatureFlagProvider"
104
+ );
105
+ }
106
+ return context;
107
+ }
108
+ function useFlagState() {
109
+ return useSyncExternalStore(
110
+ subscribeToFlagChanges,
111
+ getFlagState,
112
+ () => null
113
+ );
114
+ }
115
+
116
+ // src/hooks.ts
117
+ import { useMemo as useMemo2 } from "react";
118
+ function useFeatureFlag(flagName) {
119
+ const { isEnabled } = useFeatureFlagContext();
120
+ return isEnabled(flagName);
121
+ }
122
+ function useFeatureFlagValue(flagName) {
123
+ const { getValue } = useFeatureFlagContext();
124
+ return getValue(flagName);
125
+ }
126
+ function useFeatureFlags() {
127
+ const { flags } = useFeatureFlagContext();
128
+ return flags;
129
+ }
130
+ function useFeatureFlagControls() {
131
+ const { setFlag, resetFlags } = useFeatureFlagContext();
132
+ return useMemo2(() => ({ setFlag, resetFlags }), [setFlag, resetFlags]);
133
+ }
134
+ function FeatureFlag({
135
+ flag,
136
+ children,
137
+ fallback = null
138
+ }) {
139
+ const enabled = useFeatureFlag(flag);
140
+ return enabled ? children : fallback;
141
+ }
142
+
143
+ // src/devtools.tsx
144
+ import { useState as useState2 } from "react";
145
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
146
+ var positionStyles = {
147
+ "bottom-right": { bottom: 16, right: 16 },
148
+ "bottom-left": { bottom: 16, left: 16 },
149
+ "top-right": { top: 16, right: 16 },
150
+ "top-left": { top: 16, left: 16 }
151
+ };
152
+ var baseStyles = {
153
+ position: "fixed",
154
+ zIndex: 99999,
155
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
156
+ fontSize: 13
157
+ };
158
+ var panelStyles = {
159
+ backgroundColor: "#1a1a2e",
160
+ border: "1px solid #2d2d44",
161
+ borderRadius: 8,
162
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
163
+ overflow: "hidden",
164
+ minWidth: 280,
165
+ maxWidth: 360,
166
+ maxHeight: "70vh"
167
+ };
168
+ var headerStyles = {
169
+ display: "flex",
170
+ alignItems: "center",
171
+ justifyContent: "space-between",
172
+ padding: "10px 12px",
173
+ backgroundColor: "#16162a",
174
+ borderBottom: "1px solid #2d2d44",
175
+ cursor: "pointer",
176
+ userSelect: "none"
177
+ };
178
+ var titleStyles = {
179
+ color: "#e0e0e0",
180
+ fontWeight: 600,
181
+ fontSize: 12,
182
+ textTransform: "uppercase",
183
+ letterSpacing: "0.5px",
184
+ display: "flex",
185
+ alignItems: "center",
186
+ gap: 6
187
+ };
188
+ var contentStyles = {
189
+ padding: 0,
190
+ overflowY: "auto",
191
+ maxHeight: "calc(70vh - 50px)"
192
+ };
193
+ var flagRowStyles = {
194
+ display: "flex",
195
+ alignItems: "center",
196
+ justifyContent: "space-between",
197
+ padding: "10px 12px",
198
+ borderBottom: "1px solid #2d2d44",
199
+ transition: "background-color 0.15s"
200
+ };
201
+ var flagNameStyles = {
202
+ color: "#c0c0c0",
203
+ fontSize: 13,
204
+ fontWeight: 500,
205
+ flex: 1,
206
+ overflow: "hidden",
207
+ textOverflow: "ellipsis",
208
+ whiteSpace: "nowrap"
209
+ };
210
+ var toggleStyles = {
211
+ position: "relative",
212
+ width: 40,
213
+ height: 22,
214
+ backgroundColor: "#3d3d5c",
215
+ borderRadius: 11,
216
+ cursor: "pointer",
217
+ transition: "background-color 0.2s",
218
+ flexShrink: 0,
219
+ border: "none",
220
+ padding: 0
221
+ };
222
+ var toggleActiveStyles = {
223
+ ...toggleStyles,
224
+ backgroundColor: "#4ade80"
225
+ };
226
+ var toggleKnobStyles = {
227
+ position: "absolute",
228
+ top: 2,
229
+ left: 2,
230
+ width: 18,
231
+ height: 18,
232
+ backgroundColor: "#fff",
233
+ borderRadius: "50%",
234
+ transition: "transform 0.2s",
235
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.3)"
236
+ };
237
+ var toggleKnobActiveStyles = {
238
+ ...toggleKnobStyles,
239
+ transform: "translateX(18px)"
240
+ };
241
+ var inputStyles = {
242
+ backgroundColor: "#2d2d44",
243
+ border: "1px solid #3d3d5c",
244
+ borderRadius: 4,
245
+ color: "#e0e0e0",
246
+ padding: "4px 8px",
247
+ fontSize: 12,
248
+ width: 80,
249
+ outline: "none"
250
+ };
251
+ var badgeStyles = {
252
+ backgroundColor: "#4ade80",
253
+ color: "#1a1a2e",
254
+ fontSize: 10,
255
+ fontWeight: 700,
256
+ padding: "2px 6px",
257
+ borderRadius: 4,
258
+ marginLeft: 6
259
+ };
260
+ var overrideBadgeStyles = {
261
+ backgroundColor: "#f59e0b",
262
+ color: "#1a1a2e",
263
+ fontSize: 9,
264
+ fontWeight: 600,
265
+ padding: "1px 4px",
266
+ borderRadius: 3,
267
+ marginLeft: 6
268
+ };
269
+ var buttonStyles = {
270
+ backgroundColor: "transparent",
271
+ border: "none",
272
+ color: "#888",
273
+ cursor: "pointer",
274
+ padding: 4,
275
+ borderRadius: 4,
276
+ display: "flex",
277
+ alignItems: "center",
278
+ justifyContent: "center",
279
+ transition: "color 0.15s, background-color 0.15s"
280
+ };
281
+ var resetButtonStyles = {
282
+ backgroundColor: "#2d2d44",
283
+ border: "1px solid #3d3d5c",
284
+ borderRadius: 4,
285
+ color: "#c0c0c0",
286
+ padding: "6px 12px",
287
+ fontSize: 11,
288
+ cursor: "pointer",
289
+ margin: "8px 12px 12px",
290
+ width: "calc(100% - 24px)",
291
+ transition: "background-color 0.15s"
292
+ };
293
+ var fabStyles = {
294
+ width: 48,
295
+ height: 48,
296
+ borderRadius: "50%",
297
+ backgroundColor: "#4ade80",
298
+ border: "none",
299
+ cursor: "pointer",
300
+ display: "flex",
301
+ alignItems: "center",
302
+ justifyContent: "center",
303
+ boxShadow: "0 4px 12px rgba(74, 222, 128, 0.4)",
304
+ transition: "transform 0.15s, box-shadow 0.15s"
305
+ };
306
+ function FlagIcon() {
307
+ return /* @__PURE__ */ jsxs(
308
+ "svg",
309
+ {
310
+ width: "20",
311
+ height: "20",
312
+ viewBox: "0 0 24 24",
313
+ fill: "none",
314
+ stroke: "currentColor",
315
+ strokeWidth: "2",
316
+ strokeLinecap: "round",
317
+ strokeLinejoin: "round",
318
+ children: [
319
+ /* @__PURE__ */ jsx2("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
320
+ /* @__PURE__ */ jsx2("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
321
+ ]
322
+ }
323
+ );
324
+ }
325
+ function CloseIcon() {
326
+ return /* @__PURE__ */ jsxs(
327
+ "svg",
328
+ {
329
+ width: "16",
330
+ height: "16",
331
+ viewBox: "0 0 24 24",
332
+ fill: "none",
333
+ stroke: "currentColor",
334
+ strokeWidth: "2",
335
+ strokeLinecap: "round",
336
+ strokeLinejoin: "round",
337
+ children: [
338
+ /* @__PURE__ */ jsx2("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
339
+ /* @__PURE__ */ jsx2("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
340
+ ]
341
+ }
342
+ );
343
+ }
344
+ function FlagRow({
345
+ name,
346
+ value,
347
+ defaultValue,
348
+ onToggle,
349
+ onChange
350
+ }) {
351
+ const isOverridden = value !== defaultValue;
352
+ const isBoolean = typeof value === "boolean";
353
+ const handleInputChange = (e) => {
354
+ const newValue = e.target.value;
355
+ const num = Number(newValue);
356
+ if (!isNaN(num) && newValue !== "") {
357
+ onChange(num);
358
+ } else {
359
+ onChange(newValue);
360
+ }
361
+ };
362
+ return /* @__PURE__ */ jsxs("div", { style: flagRowStyles, children: [
363
+ /* @__PURE__ */ jsxs("span", { style: flagNameStyles, children: [
364
+ name,
365
+ isOverridden && /* @__PURE__ */ jsx2("span", { style: overrideBadgeStyles, children: "override" })
366
+ ] }),
367
+ isBoolean ? /* @__PURE__ */ jsx2(
368
+ "button",
369
+ {
370
+ style: value ? toggleActiveStyles : toggleStyles,
371
+ onClick: onToggle,
372
+ type: "button",
373
+ "aria-label": `Toggle ${name}`,
374
+ children: /* @__PURE__ */ jsx2("span", { style: value ? toggleKnobActiveStyles : toggleKnobStyles })
375
+ }
376
+ ) : /* @__PURE__ */ jsx2(
377
+ "input",
378
+ {
379
+ style: inputStyles,
380
+ type: "text",
381
+ value: String(value),
382
+ onChange: handleInputChange
383
+ }
384
+ )
385
+ ] });
386
+ }
387
+ function DevToolsInner({
388
+ position,
389
+ defaultOpen
390
+ }) {
391
+ const [isOpen, setIsOpen] = useState2(defaultOpen);
392
+ const flagState = useFlagState();
393
+ const context = useFeatureFlagContext();
394
+ if (!flagState) {
395
+ return null;
396
+ }
397
+ const { setFlag, resetFlags } = context;
398
+ const { flags, defaultFlags } = flagState;
399
+ const flagEntries = Object.entries(flags);
400
+ const overrideCount = flagEntries.filter(
401
+ ([key, value]) => value !== defaultFlags[key]
402
+ ).length;
403
+ const handleToggle = (key) => {
404
+ const currentValue = flags[key];
405
+ if (typeof currentValue === "boolean") {
406
+ setFlag(key, !currentValue);
407
+ }
408
+ };
409
+ const handleChange = (key, value) => {
410
+ setFlag(key, value);
411
+ };
412
+ if (!isOpen) {
413
+ return /* @__PURE__ */ jsx2("div", { style: { ...baseStyles, ...positionStyles[position] }, children: /* @__PURE__ */ jsx2(
414
+ "button",
415
+ {
416
+ style: fabStyles,
417
+ onClick: () => setIsOpen(true),
418
+ type: "button",
419
+ "aria-label": "Open feature flags devtools",
420
+ children: /* @__PURE__ */ jsx2("span", { style: { color: "#1a1a2e" }, children: /* @__PURE__ */ jsx2(FlagIcon, {}) })
421
+ }
422
+ ) });
423
+ }
424
+ return /* @__PURE__ */ jsx2("div", { style: { ...baseStyles, ...positionStyles[position] }, children: /* @__PURE__ */ jsxs("div", { style: panelStyles, children: [
425
+ /* @__PURE__ */ jsxs("div", { style: headerStyles, onClick: () => setIsOpen(false), children: [
426
+ /* @__PURE__ */ jsxs("span", { style: titleStyles, children: [
427
+ /* @__PURE__ */ jsx2(FlagIcon, {}),
428
+ "Feature Flags",
429
+ overrideCount > 0 && /* @__PURE__ */ jsx2("span", { style: badgeStyles, children: overrideCount })
430
+ ] }),
431
+ /* @__PURE__ */ jsx2("button", { style: buttonStyles, type: "button", "aria-label": "Close", children: /* @__PURE__ */ jsx2(CloseIcon, {}) })
432
+ ] }),
433
+ /* @__PURE__ */ jsx2("div", { style: contentStyles, children: flagEntries.map(([key, value]) => /* @__PURE__ */ jsx2(
434
+ FlagRow,
435
+ {
436
+ name: key,
437
+ value,
438
+ defaultValue: defaultFlags[key],
439
+ onToggle: () => handleToggle(key),
440
+ onChange: (newValue) => handleChange(key, newValue)
441
+ },
442
+ key
443
+ )) }),
444
+ overrideCount > 0 && /* @__PURE__ */ jsx2("button", { style: resetButtonStyles, onClick: resetFlags, type: "button", children: "Reset all overrides" })
445
+ ] }) });
446
+ }
447
+ function DevTools({
448
+ position = "bottom-right",
449
+ defaultOpen = false
450
+ }) {
451
+ const isDev = typeof __DEV__ !== "undefined" ? __DEV__ : true;
452
+ if (!isDev) {
453
+ return null;
454
+ }
455
+ return /* @__PURE__ */ jsx2(DevToolsInner, { position, defaultOpen });
456
+ }
457
+ export {
458
+ DevTools,
459
+ FeatureFlag,
460
+ FeatureFlagProvider,
461
+ useFeatureFlag,
462
+ useFeatureFlagContext,
463
+ useFeatureFlagControls,
464
+ useFeatureFlagValue,
465
+ useFeatureFlags
466
+ };
467
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context.tsx","../src/hooks.ts","../src/devtools.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport type {\n FeatureFlagContextValue,\n FeatureFlagProviderProps,\n FeatureFlags,\n} from \"./types\";\n\nconst FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);\n\nconst STORAGE_KEY_DEFAULT = \"localflag:overrides\";\n\nfunction getStoredOverrides<T extends FeatureFlags>(\n storageKey: string\n): Partial<T> {\n if (typeof window === \"undefined\") return {};\n try {\n const stored = localStorage.getItem(storageKey);\n return stored ? JSON.parse(stored) : {};\n } catch {\n return {};\n }\n}\n\nfunction setStoredOverrides<T extends FeatureFlags>(\n storageKey: string,\n overrides: Partial<T>\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (Object.keys(overrides).length === 0) {\n localStorage.removeItem(storageKey);\n } else {\n localStorage.setItem(storageKey, JSON.stringify(overrides));\n }\n } catch {\n // Ignore storage errors\n }\n}\n\n// Event emitter for devtools communication\ntype Listener = () => void;\nconst listeners = new Set<Listener>();\nlet currentState: { flags: FeatureFlags; defaultFlags: FeatureFlags } | null = null;\n\nexport function subscribeToFlagChanges(listener: Listener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getFlagState() {\n return currentState;\n}\n\nfunction notifyListeners() {\n listeners.forEach((listener) => listener());\n}\n\nexport function FeatureFlagProvider<T extends FeatureFlags>({\n children,\n defaultFlags,\n storageKey = STORAGE_KEY_DEFAULT,\n persistOverrides = true,\n}: FeatureFlagProviderProps<T>) {\n const [overrides, setOverrides] = useState<Partial<T>>(() =>\n persistOverrides ? getStoredOverrides<T>(storageKey) : {}\n );\n\n const flags = useMemo(\n () => ({ ...defaultFlags, ...overrides }) as T,\n [defaultFlags, overrides]\n );\n\n // Update global state for devtools\n useEffect(() => {\n currentState = { flags, defaultFlags };\n notifyListeners();\n }, [flags, defaultFlags]);\n\n // Persist overrides to localStorage\n useEffect(() => {\n if (persistOverrides) {\n setStoredOverrides(storageKey, overrides);\n }\n }, [overrides, storageKey, persistOverrides]);\n\n const isEnabled = useCallback(\n (flagName: keyof T): boolean => {\n const value = flags[flagName];\n return Boolean(value);\n },\n [flags]\n );\n\n const getValue = useCallback(\n <K extends keyof T>(flagName: K): T[K] => {\n return flags[flagName];\n },\n [flags]\n );\n\n const setFlag = useCallback(<K extends keyof T>(flagName: K, value: T[K]) => {\n setOverrides((prev) => ({ ...prev, [flagName]: value }));\n }, []);\n\n const resetFlags = useCallback(() => {\n setOverrides({});\n }, []);\n\n const value = useMemo<FeatureFlagContextValue<T>>(\n () => ({\n flags,\n isEnabled,\n getValue,\n setFlag,\n resetFlags,\n }),\n [flags, isEnabled, getValue, setFlag, resetFlags]\n );\n\n return (\n <FeatureFlagContext.Provider value={value as unknown as FeatureFlagContextValue}>\n {children}\n </FeatureFlagContext.Provider>\n );\n}\n\nexport function useFeatureFlagContext<\n T extends FeatureFlags = FeatureFlags\n>(): FeatureFlagContextValue<T> {\n const context = useContext(FeatureFlagContext);\n if (!context) {\n throw new Error(\n \"useFeatureFlagContext must be used within a FeatureFlagProvider\"\n );\n }\n return context as unknown as FeatureFlagContextValue<T>;\n}\n\n// Hook for devtools to subscribe to flag changes\nexport function useFlagState() {\n return useSyncExternalStore(\n subscribeToFlagChanges,\n getFlagState,\n () => null\n );\n}\n","import { useMemo } from \"react\";\nimport { useFeatureFlagContext } from \"./context\";\nimport type { FeatureFlags } from \"./types\";\n\n/**\n * Hook to check if a feature flag is enabled (truthy)\n */\nexport function useFeatureFlag<T extends FeatureFlags = FeatureFlags>(\n flagName: keyof T\n): boolean {\n const { isEnabled } = useFeatureFlagContext<T>();\n return isEnabled(flagName);\n}\n\n/**\n * Hook to get the raw value of a feature flag\n */\nexport function useFeatureFlagValue<\n T extends FeatureFlags = FeatureFlags,\n K extends keyof T = keyof T\n>(flagName: K): T[K] {\n const { getValue } = useFeatureFlagContext<T>();\n return getValue(flagName);\n}\n\n/**\n * Hook to get all feature flags\n */\nexport function useFeatureFlags<T extends FeatureFlags = FeatureFlags>(): T {\n const { flags } = useFeatureFlagContext<T>();\n return flags;\n}\n\n/**\n * Hook to get flag controls (setFlag, resetFlags)\n */\nexport function useFeatureFlagControls<T extends FeatureFlags = FeatureFlags>() {\n const { setFlag, resetFlags } = useFeatureFlagContext<T>();\n return useMemo(() => ({ setFlag, resetFlags }), [setFlag, resetFlags]);\n}\n\n/**\n * Component wrapper that renders children only if flag is enabled\n */\nexport function FeatureFlag<T extends FeatureFlags = FeatureFlags>({\n flag,\n children,\n fallback = null,\n}: {\n flag: keyof T;\n children: React.ReactNode;\n fallback?: React.ReactNode;\n}): React.ReactNode {\n const enabled = useFeatureFlag<T>(flag);\n return enabled ? children : fallback;\n}\n","import { useState } from \"react\";\nimport { useFlagState, useFeatureFlagContext } from \"./context\";\nimport type { FlagValue, FeatureFlags } from \"./types\";\n\ndeclare const __DEV__: boolean | undefined;\n\ninterface DevToolsProps {\n position?: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n defaultOpen?: boolean;\n}\n\nconst positionStyles: Record<string, React.CSSProperties> = {\n \"bottom-right\": { bottom: 16, right: 16 },\n \"bottom-left\": { bottom: 16, left: 16 },\n \"top-right\": { top: 16, right: 16 },\n \"top-left\": { top: 16, left: 16 },\n};\n\nconst baseStyles: React.CSSProperties = {\n position: \"fixed\",\n zIndex: 99999,\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace',\n fontSize: 13,\n};\n\nconst panelStyles: React.CSSProperties = {\n backgroundColor: \"#1a1a2e\",\n border: \"1px solid #2d2d44\",\n borderRadius: 8,\n boxShadow: \"0 8px 32px rgba(0, 0, 0, 0.4)\",\n overflow: \"hidden\",\n minWidth: 280,\n maxWidth: 360,\n maxHeight: \"70vh\",\n};\n\nconst headerStyles: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 12px\",\n backgroundColor: \"#16162a\",\n borderBottom: \"1px solid #2d2d44\",\n cursor: \"pointer\",\n userSelect: \"none\",\n};\n\nconst titleStyles: React.CSSProperties = {\n color: \"#e0e0e0\",\n fontWeight: 600,\n fontSize: 12,\n textTransform: \"uppercase\",\n letterSpacing: \"0.5px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n};\n\nconst contentStyles: React.CSSProperties = {\n padding: 0,\n overflowY: \"auto\",\n maxHeight: \"calc(70vh - 50px)\",\n};\n\nconst flagRowStyles: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 12px\",\n borderBottom: \"1px solid #2d2d44\",\n transition: \"background-color 0.15s\",\n};\n\nconst flagNameStyles: React.CSSProperties = {\n color: \"#c0c0c0\",\n fontSize: 13,\n fontWeight: 500,\n flex: 1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n};\n\nconst toggleStyles: React.CSSProperties = {\n position: \"relative\",\n width: 40,\n height: 22,\n backgroundColor: \"#3d3d5c\",\n borderRadius: 11,\n cursor: \"pointer\",\n transition: \"background-color 0.2s\",\n flexShrink: 0,\n border: \"none\",\n padding: 0,\n};\n\nconst toggleActiveStyles: React.CSSProperties = {\n ...toggleStyles,\n backgroundColor: \"#4ade80\",\n};\n\nconst toggleKnobStyles: React.CSSProperties = {\n position: \"absolute\",\n top: 2,\n left: 2,\n width: 18,\n height: 18,\n backgroundColor: \"#fff\",\n borderRadius: \"50%\",\n transition: \"transform 0.2s\",\n boxShadow: \"0 1px 3px rgba(0, 0, 0, 0.3)\",\n};\n\nconst toggleKnobActiveStyles: React.CSSProperties = {\n ...toggleKnobStyles,\n transform: \"translateX(18px)\",\n};\n\nconst inputStyles: React.CSSProperties = {\n backgroundColor: \"#2d2d44\",\n border: \"1px solid #3d3d5c\",\n borderRadius: 4,\n color: \"#e0e0e0\",\n padding: \"4px 8px\",\n fontSize: 12,\n width: 80,\n outline: \"none\",\n};\n\nconst badgeStyles: React.CSSProperties = {\n backgroundColor: \"#4ade80\",\n color: \"#1a1a2e\",\n fontSize: 10,\n fontWeight: 700,\n padding: \"2px 6px\",\n borderRadius: 4,\n marginLeft: 6,\n};\n\nconst overrideBadgeStyles: React.CSSProperties = {\n backgroundColor: \"#f59e0b\",\n color: \"#1a1a2e\",\n fontSize: 9,\n fontWeight: 600,\n padding: \"1px 4px\",\n borderRadius: 3,\n marginLeft: 6,\n};\n\nconst buttonStyles: React.CSSProperties = {\n backgroundColor: \"transparent\",\n border: \"none\",\n color: \"#888\",\n cursor: \"pointer\",\n padding: 4,\n borderRadius: 4,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n transition: \"color 0.15s, background-color 0.15s\",\n};\n\nconst resetButtonStyles: React.CSSProperties = {\n backgroundColor: \"#2d2d44\",\n border: \"1px solid #3d3d5c\",\n borderRadius: 4,\n color: \"#c0c0c0\",\n padding: \"6px 12px\",\n fontSize: 11,\n cursor: \"pointer\",\n margin: \"8px 12px 12px\",\n width: \"calc(100% - 24px)\",\n transition: \"background-color 0.15s\",\n};\n\nconst fabStyles: React.CSSProperties = {\n width: 48,\n height: 48,\n borderRadius: \"50%\",\n backgroundColor: \"#4ade80\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: \"0 4px 12px rgba(74, 222, 128, 0.4)\",\n transition: \"transform 0.15s, box-shadow 0.15s\",\n};\n\nfunction FlagIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z\" />\n <line x1=\"4\" y1=\"22\" x2=\"4\" y2=\"15\" />\n </svg>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\nfunction FlagRow({\n name,\n value,\n defaultValue,\n onToggle,\n onChange,\n}: {\n name: string;\n value: FlagValue;\n defaultValue: FlagValue;\n onToggle: () => void;\n onChange: (value: FlagValue) => void;\n}) {\n const isOverridden = value !== defaultValue;\n const isBoolean = typeof value === \"boolean\";\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n // Try to parse as number\n const num = Number(newValue);\n if (!isNaN(num) && newValue !== \"\") {\n onChange(num);\n } else {\n onChange(newValue);\n }\n };\n\n return (\n <div style={flagRowStyles}>\n <span style={flagNameStyles}>\n {name}\n {isOverridden && <span style={overrideBadgeStyles}>override</span>}\n </span>\n {isBoolean ? (\n <button\n style={value ? toggleActiveStyles : toggleStyles}\n onClick={onToggle}\n type=\"button\"\n aria-label={`Toggle ${name}`}\n >\n <span style={value ? toggleKnobActiveStyles : toggleKnobStyles} />\n </button>\n ) : (\n <input\n style={inputStyles}\n type=\"text\"\n value={String(value)}\n onChange={handleInputChange}\n />\n )}\n </div>\n );\n}\n\nfunction DevToolsInner({\n position,\n defaultOpen,\n}: {\n position: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n defaultOpen: boolean;\n}) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n const flagState = useFlagState();\n const context = useFeatureFlagContext<FeatureFlags>();\n\n if (!flagState) {\n return null;\n }\n\n const { setFlag, resetFlags } = context;\n\n const { flags, defaultFlags } = flagState;\n const flagEntries = Object.entries(flags);\n const overrideCount = flagEntries.filter(\n ([key, value]) => value !== defaultFlags[key]\n ).length;\n\n const handleToggle = (key: string) => {\n const currentValue = flags[key];\n if (typeof currentValue === \"boolean\") {\n setFlag(key, !currentValue);\n }\n };\n\n const handleChange = (key: string, value: FlagValue) => {\n setFlag(key, value);\n };\n\n if (!isOpen) {\n return (\n <div style={{ ...baseStyles, ...positionStyles[position] }}>\n <button\n style={fabStyles}\n onClick={() => setIsOpen(true)}\n type=\"button\"\n aria-label=\"Open feature flags devtools\"\n >\n <span style={{ color: \"#1a1a2e\" }}>\n <FlagIcon />\n </span>\n </button>\n </div>\n );\n }\n\n return (\n <div style={{ ...baseStyles, ...positionStyles[position] }}>\n <div style={panelStyles}>\n <div style={headerStyles} onClick={() => setIsOpen(false)}>\n <span style={titleStyles}>\n <FlagIcon />\n Feature Flags\n {overrideCount > 0 && (\n <span style={badgeStyles}>{overrideCount}</span>\n )}\n </span>\n <button style={buttonStyles} type=\"button\" aria-label=\"Close\">\n <CloseIcon />\n </button>\n </div>\n <div style={contentStyles}>\n {flagEntries.map(([key, value]) => (\n <FlagRow\n key={key}\n name={key}\n value={value}\n defaultValue={defaultFlags[key]}\n onToggle={() => handleToggle(key)}\n onChange={(newValue) => handleChange(key, newValue)}\n />\n ))}\n </div>\n {overrideCount > 0 && (\n <button style={resetButtonStyles} onClick={resetFlags} type=\"button\">\n Reset all overrides\n </button>\n )}\n </div>\n </div>\n );\n}\n\nexport function DevTools({\n position = \"bottom-right\",\n defaultOpen = false,\n}: DevToolsProps) {\n // Don't render in production - check via global __DEV__ or fallback to always showing\n const isDev = typeof __DEV__ !== \"undefined\" ? __DEV__ : true;\n if (!isDev) {\n return null;\n }\n\n return <DevToolsInner position={position} defaultOpen={defaultOpen} />;\n}\n\nexport default DevTools;\n"],"mappings":"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","names":["value","useMemo","useMemo","useState","jsx","useState"]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@localflag/react",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "peerDependencies": {
18
+ "react": ">=18.0.0",
19
+ "react-dom": ">=18.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/react": "^19.2.2",
23
+ "@types/react-dom": "^19.2.2",
24
+ "react": "^19.2.0",
25
+ "react-dom": "^19.2.0",
26
+ "tsup": "^8.5.0",
27
+ "typescript": "^5.9.2"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "dev": "tsup --watch",
32
+ "lint": "eslint src/",
33
+ "check-types": "tsc --noEmit"
34
+ }
35
+ }