@showwhat/configurator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gerald Yeo
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,37 @@
1
+ # @showwhat/configurator
2
+
3
+ A reusable React component library for visually editing showwhat feature flag definitions. Provides a complete rule-builder UI — bring your own store and persistence.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @showwhat/configurator
9
+ ```
10
+
11
+ Peer dependencies: React 19, React DOM 19.
12
+
13
+ ## Quick start
14
+
15
+ ```tsx
16
+ import { Configurator } from "@showwhat/configurator";
17
+ import "@showwhat/configurator/styles.css";
18
+
19
+ function App() {
20
+ return <Configurator store={myStore} />;
21
+ }
22
+ ```
23
+
24
+ ## Features
25
+
26
+ - Visual rule builder for all built-in condition types
27
+ - Drag-and-drop variation reordering
28
+ - Nested AND/OR condition groups
29
+ - Condition simulator and JSON context preview
30
+ - Store-agnostic — bring your own state management and persistence
31
+ - Exports individual components for custom layouts
32
+ - shadcn-based UI primitives included
33
+ - Tailwind CSS v4 with customisable theme tokens
34
+
35
+ ## Documentation
36
+
37
+ - [Configurator](https://showwhat.yeojz.dev/docs/configurator) — store interfaces, components, hooks, styling, and examples
@@ -0,0 +1,332 @@
1
+ import { Condition, Definition, Definitions, Variation, Presets, ConditionEvaluator, Resolution } from 'showwhat';
2
+ import { ClassValue } from 'clsx';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import * as class_variance_authority_types from 'class-variance-authority/types';
5
+ import * as React$1 from 'react';
6
+ import React__default, { Component, ReactNode, ErrorInfo, ComponentType } from 'react';
7
+ import { VariantProps } from 'class-variance-authority';
8
+ import { Select as Select$1, Separator as Separator$1, ScrollArea as ScrollArea$1, Dialog as Dialog$1, DropdownMenu as DropdownMenu$1, Label as Label$1, Switch as Switch$1, Popover as Popover$1, Tabs as Tabs$1 } from 'radix-ui';
9
+
10
+ type DefinitionListProps = {
11
+ definitions: Definitions;
12
+ selectedKey: string | null;
13
+ validationErrors?: Record<string, unknown[]>;
14
+ dirtyKeys?: string[];
15
+ onSelect: (key: string) => void;
16
+ onAdd: (key: string) => void | Promise<void>;
17
+ onRemove: (key: string) => void;
18
+ };
19
+ type DefinitionListItemProps = {
20
+ definitionKey: string;
21
+ variationCount: number;
22
+ isActive: boolean;
23
+ hasErrors: boolean;
24
+ isSelected: boolean;
25
+ isDirty?: boolean;
26
+ onSelect: () => void;
27
+ onRemove: () => void;
28
+ };
29
+ type DefinitionEditorProps = {
30
+ definitionKey: string;
31
+ definition: Definition;
32
+ validationErrors?: ValidationIssueDisplay[];
33
+ isDirty?: boolean;
34
+ isPending?: boolean;
35
+ onUpdate: (definition: Definition) => void;
36
+ onRename: (newKey: string) => void | Promise<void>;
37
+ onSave?: () => void;
38
+ onDiscard?: () => void;
39
+ };
40
+ type VariationListProps = {
41
+ variations: Variation[];
42
+ validationErrors?: ValidationIssueDisplay[];
43
+ onChange: (variations: Variation[]) => void;
44
+ };
45
+ type VariationCardProps = {
46
+ variation: Variation;
47
+ index: number;
48
+ validationErrors?: ValidationIssueDisplay[];
49
+ onChange: (variation: Variation) => void;
50
+ onRemove: () => void;
51
+ dragHandleProps?: Record<string, unknown>;
52
+ };
53
+ type ConditionBuilderProps = {
54
+ conditions: Condition[];
55
+ onChange: (conditions: Condition[]) => void;
56
+ validationErrors?: ValidationIssueDisplay[];
57
+ };
58
+ type ConditionBlockProps = {
59
+ condition: Condition;
60
+ onChange: (condition: Condition) => void;
61
+ onRemove: () => void;
62
+ onMoveUp?: () => void;
63
+ onMoveDown?: () => void;
64
+ depth?: number;
65
+ errors?: ValidationIssueDisplay[];
66
+ };
67
+ type ConditionGroupProps = {
68
+ type: "and" | "or";
69
+ conditions: Condition[];
70
+ onChange: (conditions: Condition[]) => void;
71
+ onRemove: () => void;
72
+ onMoveUp?: () => void;
73
+ onMoveDown?: () => void;
74
+ depth?: number;
75
+ errors?: ValidationIssueDisplay[];
76
+ };
77
+ type ConditionValueEditorProps = {
78
+ condition: Condition;
79
+ onChange: (condition: Condition) => void;
80
+ };
81
+ type ValueInputProps = {
82
+ value: unknown;
83
+ onChange: (value: unknown) => void;
84
+ placeholder?: string;
85
+ };
86
+ type DateTimeInputProps = {
87
+ value: string;
88
+ onChange: (value: string) => void;
89
+ };
90
+ type ValidationIssueDisplay = {
91
+ path: (string | number)[];
92
+ message: string;
93
+ };
94
+ type ValidationMessageProps = {
95
+ errors?: ValidationIssueDisplay[];
96
+ };
97
+ type ThemeToggleProps = {
98
+ theme: "light" | "dark" | "system";
99
+ onToggle: (theme: "light" | "dark" | "system") => void;
100
+ };
101
+
102
+ declare function cn(...inputs: ClassValue[]): string;
103
+
104
+ declare const AUTO_ID_PREFIX = "_tmp:";
105
+ declare function isAutoId(id: string | undefined): boolean;
106
+ declare function stripAutoIds(definitions: Definitions): Definitions;
107
+
108
+ interface ConditionTypeMeta {
109
+ type: string;
110
+ label: string;
111
+ description: string;
112
+ defaults: Record<string, unknown>;
113
+ }
114
+ declare const BUILTIN_CONDITION_TYPES: ConditionTypeMeta[];
115
+ declare const CONDITION_TYPE_MAP: Map<string, ConditionTypeMeta>;
116
+ declare function getConditionMeta(type: string): ConditionTypeMeta | undefined;
117
+
118
+ declare const buttonVariants: (props?: ({
119
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
120
+ size?: "sm" | "default" | "xs" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
121
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
122
+ declare function Button({ className, variant, size, asChild, ref, ...props }: React__default.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
123
+ asChild?: boolean;
124
+ }): react_jsx_runtime.JSX.Element;
125
+
126
+ declare function Input({ className, type, ...props }: React__default.ComponentProps<"input">): react_jsx_runtime.JSX.Element;
127
+
128
+ declare function Select({ ...props }: React__default.ComponentProps<typeof Select$1.Root>): react_jsx_runtime.JSX.Element;
129
+ declare function SelectGroup({ ...props }: React__default.ComponentProps<typeof Select$1.Group>): react_jsx_runtime.JSX.Element;
130
+ declare function SelectValue({ ...props }: React__default.ComponentProps<typeof Select$1.Value>): react_jsx_runtime.JSX.Element;
131
+ declare function SelectTrigger({ className, size, children, ...props }: React__default.ComponentProps<typeof Select$1.Trigger> & {
132
+ size?: "sm" | "default";
133
+ }): react_jsx_runtime.JSX.Element;
134
+ declare function SelectContent({ className, children, position, align, ...props }: React__default.ComponentProps<typeof Select$1.Content>): react_jsx_runtime.JSX.Element;
135
+ declare function SelectLabel({ className, ...props }: React__default.ComponentProps<typeof Select$1.Label>): react_jsx_runtime.JSX.Element;
136
+ declare function SelectItem({ className, children, ...props }: React__default.ComponentProps<typeof Select$1.Item>): react_jsx_runtime.JSX.Element;
137
+ declare function SelectSeparator({ className, ...props }: React__default.ComponentProps<typeof Select$1.Separator>): react_jsx_runtime.JSX.Element;
138
+
139
+ declare const badgeVariants: (props?: ({
140
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
141
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
142
+ declare function Badge({ className, variant, asChild, ...props }: React__default.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
143
+ asChild?: boolean;
144
+ }): react_jsx_runtime.JSX.Element;
145
+
146
+ declare function Separator({ className, orientation, decorative, ...props }: React__default.ComponentProps<typeof Separator$1.Root>): react_jsx_runtime.JSX.Element;
147
+
148
+ declare function ScrollArea({ className, children, ...props }: React__default.ComponentProps<typeof ScrollArea$1.Root>): react_jsx_runtime.JSX.Element;
149
+ declare function ScrollBar({ className, orientation, ...props }: React__default.ComponentProps<typeof ScrollArea$1.ScrollAreaScrollbar>): react_jsx_runtime.JSX.Element;
150
+
151
+ declare function Dialog({ ...props }: React__default.ComponentProps<typeof Dialog$1.Root>): react_jsx_runtime.JSX.Element;
152
+ declare function DialogTrigger({ ...props }: React__default.ComponentProps<typeof Dialog$1.Trigger>): react_jsx_runtime.JSX.Element;
153
+ declare function DialogClose({ ...props }: React__default.ComponentProps<typeof Dialog$1.Close>): react_jsx_runtime.JSX.Element;
154
+ declare function DialogContent({ className, children, showCloseButton, ...props }: React__default.ComponentProps<typeof Dialog$1.Content> & {
155
+ showCloseButton?: boolean;
156
+ }): react_jsx_runtime.JSX.Element;
157
+ declare function DialogHeader({ className, ...props }: React__default.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
158
+ declare function DialogFooter({ className, showCloseButton, children, ...props }: React__default.ComponentProps<"div"> & {
159
+ showCloseButton?: boolean;
160
+ }): react_jsx_runtime.JSX.Element;
161
+ declare function DialogTitle({ className, ...props }: React__default.ComponentProps<typeof Dialog$1.Title>): react_jsx_runtime.JSX.Element;
162
+ declare function DialogDescription({ className, ...props }: React__default.ComponentProps<typeof Dialog$1.Description>): react_jsx_runtime.JSX.Element;
163
+
164
+ declare function DropdownMenu({ ...props }: React__default.ComponentProps<typeof DropdownMenu$1.Root>): react_jsx_runtime.JSX.Element;
165
+ declare function DropdownMenuTrigger({ ...props }: React__default.ComponentProps<typeof DropdownMenu$1.Trigger>): react_jsx_runtime.JSX.Element;
166
+ declare function DropdownMenuContent({ className, sideOffset, ...props }: React__default.ComponentProps<typeof DropdownMenu$1.Content>): react_jsx_runtime.JSX.Element;
167
+ declare function DropdownMenuItem({ className, inset, variant, ...props }: React__default.ComponentProps<typeof DropdownMenu$1.Item> & {
168
+ inset?: boolean;
169
+ variant?: "default" | "destructive";
170
+ }): react_jsx_runtime.JSX.Element;
171
+ declare function DropdownMenuSeparator({ className, ...props }: React__default.ComponentProps<typeof DropdownMenu$1.Separator>): react_jsx_runtime.JSX.Element;
172
+
173
+ declare function Label({ className, ...props }: React__default.ComponentProps<typeof Label$1.Root>): react_jsx_runtime.JSX.Element;
174
+
175
+ declare function Switch({ className, ...props }: React__default.ComponentProps<typeof Switch$1.Root>): react_jsx_runtime.JSX.Element;
176
+
177
+ declare function Textarea({ className, ...props }: React__default.ComponentProps<"textarea">): react_jsx_runtime.JSX.Element;
178
+
179
+ declare function Popover({ ...props }: React__default.ComponentProps<typeof Popover$1.Root>): react_jsx_runtime.JSX.Element;
180
+ declare function PopoverTrigger({ ...props }: React__default.ComponentProps<typeof Popover$1.Trigger>): react_jsx_runtime.JSX.Element;
181
+ declare function PopoverContent({ className, align, sideOffset, ...props }: React__default.ComponentProps<typeof Popover$1.Content>): react_jsx_runtime.JSX.Element;
182
+
183
+ declare function Tabs({ ...props }: React__default.ComponentProps<typeof Tabs$1.Root>): react_jsx_runtime.JSX.Element;
184
+ declare function TabsList({ className, ...props }: React__default.ComponentProps<typeof Tabs$1.List>): react_jsx_runtime.JSX.Element;
185
+ declare function TabsTrigger({ className, ...props }: React__default.ComponentProps<typeof Tabs$1.Trigger>): react_jsx_runtime.JSX.Element;
186
+ declare function TabsContent({ className, ...props }: React__default.ComponentProps<typeof Tabs$1.Content>): react_jsx_runtime.JSX.Element;
187
+
188
+ declare function ValueInput({ value, onChange, placeholder }: ValueInputProps): react_jsx_runtime.JSX.Element;
189
+
190
+ declare function DateTimeInput({ value, onChange }: DateTimeInputProps): react_jsx_runtime.JSX.Element;
191
+
192
+ declare function ValidationMessage({ errors }: ValidationMessageProps): react_jsx_runtime.JSX.Element | null;
193
+
194
+ declare function ThemeToggle({ theme, onToggle }: ThemeToggleProps): react_jsx_runtime.JSX.Element;
195
+
196
+ type ErrorBoundaryProps = {
197
+ children: ReactNode;
198
+ /** Optional fallback to render instead of the default error UI. */
199
+ fallback?: ReactNode;
200
+ /** Called when an error is caught. Defaults to console.error. */
201
+ onError?: (error: Error, info: ErrorInfo) => void;
202
+ };
203
+ type ErrorBoundaryState = {
204
+ error: Error | null;
205
+ };
206
+ /**
207
+ * A class component error boundary that catches render errors in its subtree
208
+ * and displays a fallback UI with the error message and a "Try again" button.
209
+ */
210
+ declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
211
+ constructor(props: ErrorBoundaryProps);
212
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
213
+ componentDidCatch(error: Error, info: ErrorInfo): void;
214
+ handleRetry: () => void;
215
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | React$1.ReactPortal | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
216
+ }
217
+
218
+ type ConfirmDialogProps = {
219
+ title: string;
220
+ description: React__default.ReactNode;
221
+ actionLabel: string;
222
+ onConfirm: () => void;
223
+ open?: boolean;
224
+ onOpenChange?: (open: boolean) => void;
225
+ children: React__default.ReactNode;
226
+ };
227
+ declare function ConfirmDialog({ title, description, actionLabel, onConfirm, open, onOpenChange, children, }: ConfirmDialogProps): react_jsx_runtime.JSX.Element;
228
+
229
+ declare function ConditionBuilder({ conditions: rawConditions, onChange, validationErrors, }: ConditionBuilderProps): react_jsx_runtime.JSX.Element;
230
+
231
+ declare const VariationCard: React$1.NamedExoticComponent<VariationCardProps>;
232
+
233
+ declare function VariationList({ variations: rawVariations, validationErrors, onChange, }: VariationListProps): react_jsx_runtime.JSX.Element;
234
+
235
+ declare function DefinitionEditor({ definitionKey, definition, validationErrors, isDirty, isPending, onUpdate, onRename, onSave, onDiscard, }: DefinitionEditorProps): react_jsx_runtime.JSX.Element;
236
+
237
+ declare function DefinitionList({ definitions, selectedKey, validationErrors, dirtyKeys, onSelect, onAdd, onRemove, }: DefinitionListProps): react_jsx_runtime.JSX.Element;
238
+
239
+ interface ConditionExtensions {
240
+ extraConditionTypes: ConditionTypeMeta[];
241
+ editorOverrides: Map<string, ComponentType<ConditionValueEditorProps>>;
242
+ }
243
+
244
+ declare function createPresetConditionMeta(presets: Presets): ConditionTypeMeta[];
245
+ declare function createPresetUI(presets: Presets): ConditionExtensions;
246
+
247
+ interface ValidationIssue {
248
+ path: (string | number)[];
249
+ message: string;
250
+ }
251
+ /** Tracks whether an async store action is in flight or has failed. */
252
+ interface ActionState {
253
+ pending: boolean;
254
+ error: Error | null;
255
+ }
256
+ /** External-store source shape for subscription-backed selectors. */
257
+ interface ConfiguratorStoreSource {
258
+ getSnapshot: () => ConfiguratorStore;
259
+ subscribe: (listener: () => void) => () => void;
260
+ }
261
+ /**
262
+ * Store contract any shell must implement.
263
+ * All commands are async to support API-backed implementations.
264
+ */
265
+ interface ConfiguratorStore {
266
+ definitions: Definitions;
267
+ selectedKey: string | null;
268
+ dirtyKeys: string[];
269
+ revision: number;
270
+ validationErrors: Record<string, ValidationIssue[]>;
271
+ isKeyDirty(key: string): boolean;
272
+ selectDefinition(key: string | null): Promise<void>;
273
+ addDefinition(key: string): Promise<void>;
274
+ removeDefinition(key: string): Promise<void>;
275
+ renameDefinition(oldKey: string, newKey: string): Promise<void>;
276
+ updateDefinition(key: string, def: Definition): Promise<void>;
277
+ saveDefinition(key: string): Promise<void>;
278
+ discardDefinition(key: string): Promise<void>;
279
+ }
280
+
281
+ declare function Configurator({ store, className, emptyState, conditionExtensions, fallbackEvaluator, }: {
282
+ store: ConfiguratorStore | ConfiguratorStoreSource;
283
+ className?: string;
284
+ emptyState?: React.ReactNode;
285
+ conditionExtensions?: ConditionExtensions;
286
+ fallbackEvaluator?: ConditionEvaluator;
287
+ }): react_jsx_runtime.JSX.Element;
288
+
289
+ interface ActionStateContextValue {
290
+ actionState: ActionState;
291
+ runAction: (action: () => Promise<void>) => Promise<void>;
292
+ clearError: () => void;
293
+ }
294
+ /** Carries only the store source — changes rarely (only when the store identity changes). */
295
+ declare const StoreSourceContext: React$1.Context<ConfiguratorStoreSource | null>;
296
+ /** Carries action state (pending / error) — changes on every async action lifecycle event. */
297
+ declare const ActionStateContext: React$1.Context<ActionStateContextValue | null>;
298
+ declare function useConfiguratorStore(): ConfiguratorStore;
299
+ declare function useActionState(): ActionStateContextValue;
300
+ /**
301
+ * Returns a stable getter for the current store snapshot.
302
+ * Use this to call store methods in event handlers without
303
+ * subscribing the component to store changes.
304
+ */
305
+ declare function useStoreRef(): () => ConfiguratorStore;
306
+
307
+ /**
308
+ * Select a slice of the ConfiguratorStore with subscription-level
309
+ * short-circuiting. The component only rerenders when the selected
310
+ * value changes (via `Object.is` equality).
311
+ *
312
+ * Reads only from StoreSourceContext so action-state changes
313
+ * (pending / error) do NOT trigger rerenders in selector consumers.
314
+ */
315
+ declare function useConfiguratorSelector<T>(selector: (store: ConfiguratorStore) => T): T;
316
+
317
+ type PreviewResult = {
318
+ status: "success";
319
+ value: unknown;
320
+ meta: Resolution["meta"];
321
+ } | {
322
+ status: "inactive";
323
+ message: string;
324
+ } | {
325
+ status: "no-match";
326
+ message: string;
327
+ } | {
328
+ status: "error";
329
+ message: string;
330
+ };
331
+
332
+ export { AUTO_ID_PREFIX, type ActionState, ActionStateContext, type ActionStateContextValue, BUILTIN_CONDITION_TYPES, Badge, Button, CONDITION_TYPE_MAP, type ConditionBlockProps, ConditionBuilder, type ConditionBuilderProps, type ConditionExtensions, type ConditionGroupProps, type ConditionTypeMeta, type ConditionValueEditorProps, Configurator, type ConfiguratorStore, type ConfiguratorStoreSource, ConfirmDialog, DateTimeInput, type DateTimeInputProps, DefinitionEditor, type DefinitionEditorProps, DefinitionList, type DefinitionListItemProps, type DefinitionListProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, ErrorBoundary, Input, Label, Popover, PopoverContent, PopoverTrigger, type PreviewResult, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, StoreSourceContext, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeToggle, type ThemeToggleProps, type ValidationIssue, type ValidationIssueDisplay, ValidationMessage, type ValidationMessageProps, ValueInput, type ValueInputProps, VariationCard, type VariationCardProps, VariationList, type VariationListProps, badgeVariants, buttonVariants, cn, createPresetConditionMeta, createPresetUI, getConditionMeta, isAutoId, stripAutoIds, useActionState, useConfiguratorSelector, useConfiguratorStore, useStoreRef };