@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 +21 -0
- package/README.md +170 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +467 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|