@flanksource/clicky-ui 0.3.31 → 0.3.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/comments/CommentCard.cjs +79 -44
- package/dist/comments/CommentCard.cjs.map +1 -1
- package/dist/comments/CommentCard.d.ts +2 -0
- package/dist/comments/CommentCard.d.ts.map +1 -1
- package/dist/comments/CommentCard.js +81 -46
- package/dist/comments/CommentCard.js.map +1 -1
- package/dist/comments/CommentCardParts.cjs +20 -0
- package/dist/comments/CommentCardParts.cjs.map +1 -1
- package/dist/comments/CommentCardParts.d.ts +4 -0
- package/dist/comments/CommentCardParts.d.ts.map +1 -1
- package/dist/comments/CommentCardParts.js +22 -2
- package/dist/comments/CommentCardParts.js.map +1 -1
- package/dist/comments/CommentProvider.cjs +3 -0
- package/dist/comments/CommentProvider.cjs.map +1 -1
- package/dist/comments/CommentProvider.d.ts +1 -1
- package/dist/comments/CommentProvider.d.ts.map +1 -1
- package/dist/comments/CommentProvider.js +3 -0
- package/dist/comments/CommentProvider.js.map +1 -1
- package/dist/comments/CommentSidePanel.cjs +43 -17
- package/dist/comments/CommentSidePanel.cjs.map +1 -1
- package/dist/comments/CommentSidePanel.d.ts.map +1 -1
- package/dist/comments/CommentSidePanel.js +44 -18
- package/dist/comments/CommentSidePanel.js.map +1 -1
- package/dist/comments/CommentThreadList.cjs +2 -0
- package/dist/comments/CommentThreadList.cjs.map +1 -1
- package/dist/comments/CommentThreadList.d.ts +3 -2
- package/dist/comments/CommentThreadList.d.ts.map +1 -1
- package/dist/comments/CommentThreadList.js +2 -0
- package/dist/comments/CommentThreadList.js.map +1 -1
- package/dist/comments/comment-types.cjs +16 -4
- package/dist/comments/comment-types.cjs.map +1 -1
- package/dist/comments/comment-types.d.ts +11 -1
- package/dist/comments/comment-types.d.ts.map +1 -1
- package/dist/comments/comment-types.js +16 -4
- package/dist/comments/comment-types.js.map +1 -1
- package/dist/comments/comment-utils.cjs +31 -1
- package/dist/comments/comment-utils.cjs.map +1 -1
- package/dist/comments/comment-utils.d.ts +9 -2
- package/dist/comments/comment-utils.d.ts.map +1 -1
- package/dist/comments/comment-utils.js +31 -1
- package/dist/comments/comment-utils.js.map +1 -1
- package/dist/comments.cjs +3 -0
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js +4 -1
- package/dist/components/EndpointSelector.cjs +6 -6
- package/dist/components/EndpointSelector.cjs.map +1 -1
- package/dist/components/EndpointSelector.d.ts.map +1 -1
- package/dist/components/EndpointSelector.js +6 -6
- package/dist/components/EndpointSelector.js.map +1 -1
- package/dist/components/ErrorWrapper.cjs +3 -2
- package/dist/components/ErrorWrapper.cjs.map +1 -1
- package/dist/components/ErrorWrapper.js +3 -2
- package/dist/components/ErrorWrapper.js.map +1 -1
- package/dist/components/RangeSlider.cjs +108 -45
- package/dist/components/RangeSlider.cjs.map +1 -1
- package/dist/components/RangeSlider.d.ts +3 -1
- package/dist/components/RangeSlider.d.ts.map +1 -1
- package/dist/components/RangeSlider.js +108 -45
- package/dist/components/RangeSlider.js.map +1 -1
- package/dist/components/SecretKeySelector.cjs +4 -4
- package/dist/components/SecretKeySelector.cjs.map +1 -1
- package/dist/components/SecretKeySelector.js +4 -4
- package/dist/components/SecretKeySelector.js.map +1 -1
- package/dist/data/test-runner/status.cjs +1 -1
- package/dist/data/test-runner/status.cjs.map +1 -1
- package/dist/data/test-runner/status.js +1 -1
- package/dist/data/test-runner/status.js.map +1 -1
- package/dist/data/test-runner/types.d.ts +6 -0
- package/dist/data/test-runner/types.d.ts.map +1 -1
- package/dist/data/verification/verifyReportTests.cjs +1 -0
- package/dist/data/verification/verifyReportTests.cjs.map +1 -1
- package/dist/data/verification/verifyReportTests.d.ts.map +1 -1
- package/dist/data/verification/verifyReportTests.js +1 -0
- package/dist/data/verification/verifyReportTests.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.cjs","sources":["../../src/components/RangeSlider.tsx"],"sourcesContent":["import { cn } from \"../lib/utils\";\n\nexport type RangeSliderValue = [number, number];\n\nexport type RangeSliderProps = {\n min: number;\n max: number;\n value: RangeSliderValue;\n onChange: (value: RangeSliderValue) => void;\n step?: number;\n ariaLabelMin?: string;\n ariaLabelMax?: string;\n className?: string;\n trackClassName?: string;\n rangeClassName?: string;\n thumbClassName?: string;\n};\n\nconst thumbClassName =\n \"[&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:mt-[-5px] [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-background/80 [&::-webkit-slider-thumb]:bg-muted-foreground [&::-webkit-slider-thumb]:shadow-sm [&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:size-3.5 [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-background/80 [&::-moz-range-thumb]:bg-muted-foreground [&::-moz-range-thumb]:shadow-sm\";\n\nexport function RangeSlider({\n min,\n max,\n value,\n onChange,\n step = 1,\n ariaLabelMin = \"Minimum value\",\n ariaLabelMax = \"Maximum value\",\n className,\n trackClassName,\n rangeClassName,\n thumbClassName: thumbOverrideClassName,\n}: RangeSliderProps) {\n const [lower, upper] = normalizeRangeValue(value, min, max);\n\n return (\n <div className={cn(\"
|
|
1
|
+
{"version":3,"file":"RangeSlider.cjs","sources":["../../src/components/RangeSlider.tsx"],"sourcesContent":["import { cn } from \"../lib/utils\";\n\nexport type RangeSliderValue = [number, number];\n\nexport type RangeSliderProps = {\n min: number;\n max: number;\n value: RangeSliderValue;\n onChange: (value: RangeSliderValue) => void;\n step?: number;\n ariaLabelMin?: string;\n ariaLabelMax?: string;\n /** Shows number fields below the track for exact keyboard entry. */\n editable?: boolean;\n className?: string;\n trackClassName?: string;\n rangeClassName?: string;\n thumbClassName?: string;\n};\n\nconst thumbClassName =\n \"[&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:mt-[-5px] [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-background/80 [&::-webkit-slider-thumb]:bg-muted-foreground [&::-webkit-slider-thumb]:shadow-sm [&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:size-3.5 [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-background/80 [&::-moz-range-thumb]:bg-muted-foreground [&::-moz-range-thumb]:shadow-sm\";\n\nexport function RangeSlider({\n min,\n max,\n value,\n onChange,\n step = 1,\n ariaLabelMin = \"Minimum value\",\n ariaLabelMax = \"Maximum value\",\n editable = false,\n className,\n trackClassName,\n rangeClassName,\n thumbClassName: thumbOverrideClassName,\n}: RangeSliderProps) {\n const [lower, upper] = normalizeRangeValue(value, min, max);\n\n return (\n <div className={cn(editable && \"space-y-2\", className)}>\n <div className=\"relative h-6\">\n <div\n className={cn(\n \"absolute left-0 right-0 top-1/2 h-1 -translate-y-1/2 rounded-full bg-border\",\n trackClassName,\n )}\n />\n <div\n className={cn(\n \"absolute top-1/2 h-1 -translate-y-1/2 rounded-full bg-primary/70\",\n rangeClassName,\n )}\n style={{\n left: `${valueToPercent(lower, min, max)}%`,\n right: `${100 - valueToPercent(upper, min, max)}%`,\n }}\n />\n <input\n type=\"range\"\n aria-label={ariaLabelMin}\n min={min}\n max={max}\n step={step}\n value={lower}\n className={cn(baseSliderClassName, thumbClassName, thumbOverrideClassName)}\n style={{ zIndex: lower >= max ? 30 : 20 }}\n onChange={(event) => {\n const nextLower = clampNumber(Number(event.target.value), min, max);\n onChange([Math.min(nextLower, upper), upper]);\n }}\n />\n <input\n type=\"range\"\n aria-label={ariaLabelMax}\n min={min}\n max={max}\n step={step}\n value={upper}\n className={cn(baseSliderClassName, thumbClassName, thumbOverrideClassName)}\n onChange={(event) => {\n const nextUpper = clampNumber(Number(event.target.value), min, max);\n onChange([lower, Math.max(nextUpper, lower)]);\n }}\n />\n </div>\n {editable ? (\n <div className=\"grid grid-cols-2 gap-2\">\n <EditableBound\n label=\"Min\"\n ariaLabel={`Edit ${lowercaseFirst(ariaLabelMin)}`}\n min={min}\n max={upper}\n step={step}\n value={lower}\n onChange={(next) => onChange([next, upper])}\n />\n <EditableBound\n label=\"Max\"\n ariaLabel={`Edit ${lowercaseFirst(ariaLabelMax)}`}\n min={lower}\n max={max}\n step={step}\n value={upper}\n onChange={(next) => onChange([lower, next])}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction EditableBound({\n label,\n ariaLabel,\n min,\n max,\n step,\n value,\n onChange,\n}: {\n label: string;\n ariaLabel: string;\n min: number;\n max: number;\n step: number;\n value: number;\n onChange: (value: number) => void;\n}) {\n return (\n <label className=\"grid grid-cols-[auto_minmax(0,1fr)] items-center gap-1.5 text-[11px] text-muted-foreground\">\n <span>{label}</span>\n <input\n aria-label={ariaLabel}\n className=\"h-7 min-w-0 rounded-md border border-input bg-background px-2 text-right font-mono text-xs tabular-nums text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n max={max}\n min={min}\n step={step}\n type=\"number\"\n value={value}\n onChange={(event) => {\n if (event.currentTarget.value === \"\") return;\n const next = Number(event.currentTarget.value);\n if (!Number.isFinite(next)) throw new Error(`${ariaLabel} must be a finite number`);\n onChange(clampNumber(next, min, max));\n }}\n />\n </label>\n );\n}\n\nconst baseSliderClassName =\n \"pointer-events-none absolute inset-0 h-6 w-full appearance-none bg-transparent [&::-webkit-slider-runnable-track]:h-1 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-transparent [&::-moz-range-track]:h-1 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-transparent\";\n\nfunction normalizeRangeValue(value: RangeSliderValue, min: number, max: number): RangeSliderValue {\n const lower = clampNumber(Math.min(value[0], value[1]), min, max);\n const upper = clampNumber(Math.max(value[0], value[1]), min, max);\n return [lower, upper];\n}\n\nfunction clampNumber(value: number, min: number, max: number) {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction valueToPercent(value: number, min: number, max: number) {\n if (max <= min) return 0;\n return ((value - min) / (max - min)) * 100;\n}\n\nfunction lowercaseFirst(value: string): string {\n return value.charAt(0).toLowerCase() + value.slice(1);\n}\n"],"names":["cn","jsxs","jsx"],"mappings":";;;;AAoBA,MAAM,iBACJ;AAEK,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,eAAe;AAAA,EACf,eAAe;AAAA,EACf,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAAqB;AACnB,QAAM,CAAC,OAAO,KAAK,IAAI,oBAAoB,OAAO,KAAK,GAAG;AAE1D,yCACG,OAAA,EAAI,WAAWA,MAAAA,GAAG,YAAY,aAAa,SAAS,GACnD,UAAA;AAAA,IAAAC,2BAAAA,KAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,MAAAC,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAWF,MAAAA;AAAAA,YACT;AAAA,YACA;AAAA,UAAA;AAAA,QACF;AAAA,MAAA;AAAA,MAEFE,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAWF,MAAAA;AAAAA,YACT;AAAA,YACA;AAAA,UAAA;AAAA,UAEF,OAAO;AAAA,YACL,MAAM,GAAG,eAAe,OAAO,KAAK,GAAG,CAAC;AAAA,YACxC,OAAO,GAAG,MAAM,eAAe,OAAO,KAAK,GAAG,CAAC;AAAA,UAAA;AAAA,QACjD;AAAA,MAAA;AAAA,MAEFE,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO;AAAA,UACP,WAAWF,MAAAA,GAAG,qBAAqB,gBAAgB,sBAAsB;AAAA,UACzE,OAAO,EAAE,QAAQ,SAAS,MAAM,KAAK,GAAA;AAAA,UACrC,UAAU,CAAC,UAAU;AACnB,kBAAM,YAAY,YAAY,OAAO,MAAM,OAAO,KAAK,GAAG,KAAK,GAAG;AAClE,qBAAS,CAAC,KAAK,IAAI,WAAW,KAAK,GAAG,KAAK,CAAC;AAAA,UAC9C;AAAA,QAAA;AAAA,MAAA;AAAA,MAEFE,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO;AAAA,UACP,WAAWF,MAAAA,GAAG,qBAAqB,gBAAgB,sBAAsB;AAAA,UACzE,UAAU,CAAC,UAAU;AACnB,kBAAM,YAAY,YAAY,OAAO,MAAM,OAAO,KAAK,GAAG,KAAK,GAAG;AAClE,qBAAS,CAAC,OAAO,KAAK,IAAI,WAAW,KAAK,CAAC,CAAC;AAAA,UAC9C;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GACF;AAAA,IACC,WACCC,2BAAAA,KAAC,OAAA,EAAI,WAAU,0BACb,UAAA;AAAA,MAAAC,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACN,WAAW,QAAQ,eAAe,YAAY,CAAC;AAAA,UAC/C;AAAA,UACA,KAAK;AAAA,UACL;AAAA,UACA,OAAO;AAAA,UACP,UAAU,CAAC,SAAS,SAAS,CAAC,MAAM,KAAK,CAAC;AAAA,QAAA;AAAA,MAAA;AAAA,MAE5CA,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACN,WAAW,QAAQ,eAAe,YAAY,CAAC;AAAA,UAC/C,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,OAAO;AAAA,UACP,UAAU,CAAC,SAAS,SAAS,CAAC,OAAO,IAAI,CAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IAC5C,EAAA,CACF,IACE;AAAA,EAAA,GACN;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,SACED,2BAAAA,KAAC,SAAA,EAAM,WAAU,8FACf,UAAA;AAAA,IAAAC,2BAAAA,IAAC,UAAM,UAAA,MAAA,CAAM;AAAA,IACbA,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,cAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAK;AAAA,QACL;AAAA,QACA,UAAU,CAAC,UAAU;AACnB,cAAI,MAAM,cAAc,UAAU,GAAI;AACtC,gBAAM,OAAO,OAAO,MAAM,cAAc,KAAK;AAC7C,cAAI,CAAC,OAAO,SAAS,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,0BAA0B;AAClF,mBAAS,YAAY,MAAM,KAAK,GAAG,CAAC;AAAA,QACtC;AAAA,MAAA;AAAA,IAAA;AAAA,EACF,GACF;AAEJ;AAEA,MAAM,sBACJ;AAEF,SAAS,oBAAoB,OAAyB,KAAa,KAA+B;AAChG,QAAM,QAAQ,YAAY,KAAK,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,GAAG;AAChE,QAAM,QAAQ,YAAY,KAAK,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,GAAG;AAChE,SAAO,CAAC,OAAO,KAAK;AACtB;AAEA,SAAS,YAAY,OAAe,KAAa,KAAa;AAC5D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAEA,SAAS,eAAe,OAAe,KAAa,KAAa;AAC/D,MAAI,OAAO,IAAK,QAAO;AACvB,UAAS,QAAQ,QAAQ,MAAM,OAAQ;AACzC;AAEA,SAAS,eAAe,OAAuB;AAC7C,SAAO,MAAM,OAAO,CAAC,EAAE,gBAAgB,MAAM,MAAM,CAAC;AACtD;;"}
|
|
@@ -7,10 +7,12 @@ export type RangeSliderProps = {
|
|
|
7
7
|
step?: number;
|
|
8
8
|
ariaLabelMin?: string;
|
|
9
9
|
ariaLabelMax?: string;
|
|
10
|
+
/** Shows number fields below the track for exact keyboard entry. */
|
|
11
|
+
editable?: boolean;
|
|
10
12
|
className?: string;
|
|
11
13
|
trackClassName?: string;
|
|
12
14
|
rangeClassName?: string;
|
|
13
15
|
thumbClassName?: string;
|
|
14
16
|
};
|
|
15
|
-
export declare function RangeSlider({ min, max, value, onChange, step, ariaLabelMin, ariaLabelMax, className, trackClassName, rangeClassName, thumbClassName: thumbOverrideClassName, }: RangeSliderProps): import("react").JSX.Element;
|
|
17
|
+
export declare function RangeSlider({ min, max, value, onChange, step, ariaLabelMin, ariaLabelMax, editable, className, trackClassName, rangeClassName, thumbClassName: thumbOverrideClassName, }: RangeSliderProps): import("react").JSX.Element;
|
|
16
18
|
//# sourceMappingURL=RangeSlider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../src/components/RangeSlider.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAKF,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,GAAG,EACH,KAAK,EACL,QAAQ,EACR,IAAQ,EACR,YAA8B,EAC9B,YAA8B,EAC9B,SAAS,EACT,cAAc,EACd,cAAc,EACd,cAAc,EAAE,sBAAsB,GACvC,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../src/components/RangeSlider.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAKF,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,GAAG,EACH,KAAK,EACL,QAAQ,EACR,IAAQ,EACR,YAA8B,EAC9B,YAA8B,EAC9B,QAAgB,EAChB,SAAS,EACT,cAAc,EACd,cAAc,EACd,cAAc,EAAE,sBAAsB,GACvC,EAAE,gBAAgB,+BA0ElB"}
|
|
@@ -9,65 +9,125 @@ function RangeSlider({
|
|
|
9
9
|
step = 1,
|
|
10
10
|
ariaLabelMin = "Minimum value",
|
|
11
11
|
ariaLabelMax = "Maximum value",
|
|
12
|
+
editable = false,
|
|
12
13
|
className,
|
|
13
14
|
trackClassName,
|
|
14
15
|
rangeClassName,
|
|
15
16
|
thumbClassName: thumbOverrideClassName
|
|
16
17
|
}) {
|
|
17
18
|
const [lower, upper] = normalizeRangeValue(value, min, max);
|
|
18
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("
|
|
19
|
-
/* @__PURE__ */
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
),
|
|
28
|
-
/* @__PURE__ */ jsx(
|
|
29
|
-
"div",
|
|
30
|
-
{
|
|
31
|
-
className: cn(
|
|
32
|
-
"absolute top-1/2 h-1 -translate-y-1/2 rounded-full bg-primary/70",
|
|
33
|
-
rangeClassName
|
|
34
|
-
),
|
|
35
|
-
style: {
|
|
36
|
-
left: `${valueToPercent(lower, min, max)}%`,
|
|
37
|
-
right: `${100 - valueToPercent(upper, min, max)}%`
|
|
19
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(editable && "space-y-2", className), children: [
|
|
20
|
+
/* @__PURE__ */ jsxs("div", { className: "relative h-6", children: [
|
|
21
|
+
/* @__PURE__ */ jsx(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: cn(
|
|
25
|
+
"absolute left-0 right-0 top-1/2 h-1 -translate-y-1/2 rounded-full bg-border",
|
|
26
|
+
trackClassName
|
|
27
|
+
)
|
|
38
28
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
style: { zIndex: lower >= max ? 30 : 20 },
|
|
52
|
-
onChange: (event) => {
|
|
53
|
-
const nextLower = clampNumber(Number(event.target.value), min, max);
|
|
54
|
-
onChange([Math.min(nextLower, upper), upper]);
|
|
29
|
+
),
|
|
30
|
+
/* @__PURE__ */ jsx(
|
|
31
|
+
"div",
|
|
32
|
+
{
|
|
33
|
+
className: cn(
|
|
34
|
+
"absolute top-1/2 h-1 -translate-y-1/2 rounded-full bg-primary/70",
|
|
35
|
+
rangeClassName
|
|
36
|
+
),
|
|
37
|
+
style: {
|
|
38
|
+
left: `${valueToPercent(lower, min, max)}%`,
|
|
39
|
+
right: `${100 - valueToPercent(upper, min, max)}%`
|
|
40
|
+
}
|
|
55
41
|
}
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
),
|
|
43
|
+
/* @__PURE__ */ jsx(
|
|
44
|
+
"input",
|
|
45
|
+
{
|
|
46
|
+
type: "range",
|
|
47
|
+
"aria-label": ariaLabelMin,
|
|
48
|
+
min,
|
|
49
|
+
max,
|
|
50
|
+
step,
|
|
51
|
+
value: lower,
|
|
52
|
+
className: cn(baseSliderClassName, thumbClassName, thumbOverrideClassName),
|
|
53
|
+
style: { zIndex: lower >= max ? 30 : 20 },
|
|
54
|
+
onChange: (event) => {
|
|
55
|
+
const nextLower = clampNumber(Number(event.target.value), min, max);
|
|
56
|
+
onChange([Math.min(nextLower, upper), upper]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
),
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
"input",
|
|
62
|
+
{
|
|
63
|
+
type: "range",
|
|
64
|
+
"aria-label": ariaLabelMax,
|
|
65
|
+
min,
|
|
66
|
+
max,
|
|
67
|
+
step,
|
|
68
|
+
value: upper,
|
|
69
|
+
className: cn(baseSliderClassName, thumbClassName, thumbOverrideClassName),
|
|
70
|
+
onChange: (event) => {
|
|
71
|
+
const nextUpper = clampNumber(Number(event.target.value), min, max);
|
|
72
|
+
onChange([lower, Math.max(nextUpper, lower)]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
] }),
|
|
77
|
+
editable ? /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
78
|
+
/* @__PURE__ */ jsx(
|
|
79
|
+
EditableBound,
|
|
80
|
+
{
|
|
81
|
+
label: "Min",
|
|
82
|
+
ariaLabel: `Edit ${lowercaseFirst(ariaLabelMin)}`,
|
|
83
|
+
min,
|
|
84
|
+
max: upper,
|
|
85
|
+
step,
|
|
86
|
+
value: lower,
|
|
87
|
+
onChange: (next) => onChange([next, upper])
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsx(
|
|
91
|
+
EditableBound,
|
|
92
|
+
{
|
|
93
|
+
label: "Max",
|
|
94
|
+
ariaLabel: `Edit ${lowercaseFirst(ariaLabelMax)}`,
|
|
95
|
+
min: lower,
|
|
96
|
+
max,
|
|
97
|
+
step,
|
|
98
|
+
value: upper,
|
|
99
|
+
onChange: (next) => onChange([lower, next])
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] }) : null
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
function EditableBound({
|
|
106
|
+
label,
|
|
107
|
+
ariaLabel,
|
|
108
|
+
min,
|
|
109
|
+
max,
|
|
110
|
+
step,
|
|
111
|
+
value,
|
|
112
|
+
onChange
|
|
113
|
+
}) {
|
|
114
|
+
return /* @__PURE__ */ jsxs("label", { className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
115
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
58
116
|
/* @__PURE__ */ jsx(
|
|
59
117
|
"input",
|
|
60
118
|
{
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
min,
|
|
119
|
+
"aria-label": ariaLabel,
|
|
120
|
+
className: "h-7 min-w-0 rounded-md border border-input bg-background px-2 text-right font-mono text-xs tabular-nums text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
64
121
|
max,
|
|
122
|
+
min,
|
|
65
123
|
step,
|
|
66
|
-
|
|
67
|
-
|
|
124
|
+
type: "number",
|
|
125
|
+
value,
|
|
68
126
|
onChange: (event) => {
|
|
69
|
-
|
|
70
|
-
|
|
127
|
+
if (event.currentTarget.value === "") return;
|
|
128
|
+
const next = Number(event.currentTarget.value);
|
|
129
|
+
if (!Number.isFinite(next)) throw new Error(`${ariaLabel} must be a finite number`);
|
|
130
|
+
onChange(clampNumber(next, min, max));
|
|
71
131
|
}
|
|
72
132
|
}
|
|
73
133
|
)
|
|
@@ -86,6 +146,9 @@ function valueToPercent(value, min, max) {
|
|
|
86
146
|
if (max <= min) return 0;
|
|
87
147
|
return (value - min) / (max - min) * 100;
|
|
88
148
|
}
|
|
149
|
+
function lowercaseFirst(value) {
|
|
150
|
+
return value.charAt(0).toLowerCase() + value.slice(1);
|
|
151
|
+
}
|
|
89
152
|
export {
|
|
90
153
|
RangeSlider
|
|
91
154
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.js","sources":["../../src/components/RangeSlider.tsx"],"sourcesContent":["import { cn } from \"../lib/utils\";\n\nexport type RangeSliderValue = [number, number];\n\nexport type RangeSliderProps = {\n min: number;\n max: number;\n value: RangeSliderValue;\n onChange: (value: RangeSliderValue) => void;\n step?: number;\n ariaLabelMin?: string;\n ariaLabelMax?: string;\n className?: string;\n trackClassName?: string;\n rangeClassName?: string;\n thumbClassName?: string;\n};\n\nconst thumbClassName =\n \"[&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:mt-[-5px] [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-background/80 [&::-webkit-slider-thumb]:bg-muted-foreground [&::-webkit-slider-thumb]:shadow-sm [&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:size-3.5 [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-background/80 [&::-moz-range-thumb]:bg-muted-foreground [&::-moz-range-thumb]:shadow-sm\";\n\nexport function RangeSlider({\n min,\n max,\n value,\n onChange,\n step = 1,\n ariaLabelMin = \"Minimum value\",\n ariaLabelMax = \"Maximum value\",\n className,\n trackClassName,\n rangeClassName,\n thumbClassName: thumbOverrideClassName,\n}: RangeSliderProps) {\n const [lower, upper] = normalizeRangeValue(value, min, max);\n\n return (\n <div className={cn(\"
|
|
1
|
+
{"version":3,"file":"RangeSlider.js","sources":["../../src/components/RangeSlider.tsx"],"sourcesContent":["import { cn } from \"../lib/utils\";\n\nexport type RangeSliderValue = [number, number];\n\nexport type RangeSliderProps = {\n min: number;\n max: number;\n value: RangeSliderValue;\n onChange: (value: RangeSliderValue) => void;\n step?: number;\n ariaLabelMin?: string;\n ariaLabelMax?: string;\n /** Shows number fields below the track for exact keyboard entry. */\n editable?: boolean;\n className?: string;\n trackClassName?: string;\n rangeClassName?: string;\n thumbClassName?: string;\n};\n\nconst thumbClassName =\n \"[&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:mt-[-5px] [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-background/80 [&::-webkit-slider-thumb]:bg-muted-foreground [&::-webkit-slider-thumb]:shadow-sm [&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:size-3.5 [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-background/80 [&::-moz-range-thumb]:bg-muted-foreground [&::-moz-range-thumb]:shadow-sm\";\n\nexport function RangeSlider({\n min,\n max,\n value,\n onChange,\n step = 1,\n ariaLabelMin = \"Minimum value\",\n ariaLabelMax = \"Maximum value\",\n editable = false,\n className,\n trackClassName,\n rangeClassName,\n thumbClassName: thumbOverrideClassName,\n}: RangeSliderProps) {\n const [lower, upper] = normalizeRangeValue(value, min, max);\n\n return (\n <div className={cn(editable && \"space-y-2\", className)}>\n <div className=\"relative h-6\">\n <div\n className={cn(\n \"absolute left-0 right-0 top-1/2 h-1 -translate-y-1/2 rounded-full bg-border\",\n trackClassName,\n )}\n />\n <div\n className={cn(\n \"absolute top-1/2 h-1 -translate-y-1/2 rounded-full bg-primary/70\",\n rangeClassName,\n )}\n style={{\n left: `${valueToPercent(lower, min, max)}%`,\n right: `${100 - valueToPercent(upper, min, max)}%`,\n }}\n />\n <input\n type=\"range\"\n aria-label={ariaLabelMin}\n min={min}\n max={max}\n step={step}\n value={lower}\n className={cn(baseSliderClassName, thumbClassName, thumbOverrideClassName)}\n style={{ zIndex: lower >= max ? 30 : 20 }}\n onChange={(event) => {\n const nextLower = clampNumber(Number(event.target.value), min, max);\n onChange([Math.min(nextLower, upper), upper]);\n }}\n />\n <input\n type=\"range\"\n aria-label={ariaLabelMax}\n min={min}\n max={max}\n step={step}\n value={upper}\n className={cn(baseSliderClassName, thumbClassName, thumbOverrideClassName)}\n onChange={(event) => {\n const nextUpper = clampNumber(Number(event.target.value), min, max);\n onChange([lower, Math.max(nextUpper, lower)]);\n }}\n />\n </div>\n {editable ? (\n <div className=\"grid grid-cols-2 gap-2\">\n <EditableBound\n label=\"Min\"\n ariaLabel={`Edit ${lowercaseFirst(ariaLabelMin)}`}\n min={min}\n max={upper}\n step={step}\n value={lower}\n onChange={(next) => onChange([next, upper])}\n />\n <EditableBound\n label=\"Max\"\n ariaLabel={`Edit ${lowercaseFirst(ariaLabelMax)}`}\n min={lower}\n max={max}\n step={step}\n value={upper}\n onChange={(next) => onChange([lower, next])}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction EditableBound({\n label,\n ariaLabel,\n min,\n max,\n step,\n value,\n onChange,\n}: {\n label: string;\n ariaLabel: string;\n min: number;\n max: number;\n step: number;\n value: number;\n onChange: (value: number) => void;\n}) {\n return (\n <label className=\"grid grid-cols-[auto_minmax(0,1fr)] items-center gap-1.5 text-[11px] text-muted-foreground\">\n <span>{label}</span>\n <input\n aria-label={ariaLabel}\n className=\"h-7 min-w-0 rounded-md border border-input bg-background px-2 text-right font-mono text-xs tabular-nums text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n max={max}\n min={min}\n step={step}\n type=\"number\"\n value={value}\n onChange={(event) => {\n if (event.currentTarget.value === \"\") return;\n const next = Number(event.currentTarget.value);\n if (!Number.isFinite(next)) throw new Error(`${ariaLabel} must be a finite number`);\n onChange(clampNumber(next, min, max));\n }}\n />\n </label>\n );\n}\n\nconst baseSliderClassName =\n \"pointer-events-none absolute inset-0 h-6 w-full appearance-none bg-transparent [&::-webkit-slider-runnable-track]:h-1 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-transparent [&::-moz-range-track]:h-1 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-transparent\";\n\nfunction normalizeRangeValue(value: RangeSliderValue, min: number, max: number): RangeSliderValue {\n const lower = clampNumber(Math.min(value[0], value[1]), min, max);\n const upper = clampNumber(Math.max(value[0], value[1]), min, max);\n return [lower, upper];\n}\n\nfunction clampNumber(value: number, min: number, max: number) {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction valueToPercent(value: number, min: number, max: number) {\n if (max <= min) return 0;\n return ((value - min) / (max - min)) * 100;\n}\n\nfunction lowercaseFirst(value: string): string {\n return value.charAt(0).toLowerCase() + value.slice(1);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -11,8 +11,8 @@ const UiEdit = require("../icons/components/UiEdit.cjs");
|
|
|
11
11
|
const DEFAULT_SOURCES = ["secret", "configmap", "value"];
|
|
12
12
|
const SHRINKABLE_FIELD = "max-w-full min-w-0 shrink";
|
|
13
13
|
const SOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;
|
|
14
|
-
const RESOURCE_FIELD = `${SHRINKABLE_FIELD} basis-
|
|
15
|
-
const KEY_FIELD = `${SHRINKABLE_FIELD} basis-
|
|
14
|
+
const RESOURCE_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow-0 @min-[22rem]/secret:basis-40`;
|
|
15
|
+
const KEY_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow @min-[22rem]/secret:basis-72`;
|
|
16
16
|
const VALUE_FIELD = `${SHRINKABLE_FIELD} basis-64 grow`;
|
|
17
17
|
function SecretKeySelector({
|
|
18
18
|
value,
|
|
@@ -126,7 +126,7 @@ function SecretKeySelector({
|
|
|
126
126
|
"div",
|
|
127
127
|
{
|
|
128
128
|
"data-slot": "secret-key-selector",
|
|
129
|
-
className: utils.cn("@container w-full min-w-0", className),
|
|
129
|
+
className: utils.cn("@container/secret w-full min-w-0", className),
|
|
130
130
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
131
131
|
"div",
|
|
132
132
|
{
|
|
@@ -213,7 +213,7 @@ function SecretKeySelector({
|
|
|
213
213
|
"div",
|
|
214
214
|
{
|
|
215
215
|
className: utils.cn(
|
|
216
|
-
"flex flex-
|
|
216
|
+
"flex flex-col items-center gap-2 @min-[22rem]/secret:flex-row",
|
|
217
217
|
SHRINKABLE_FIELD,
|
|
218
218
|
"grow"
|
|
219
219
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretKeySelector.cjs","sources":["../../src/components/SecretKeySelector.tsx"],"sourcesContent":["import { createElement, useEffect, useMemo, useState } from \"react\";\nimport {\n K8SSecret,\n K8SConfigmap,\n Helm,\n K8SServiceaccount,\n Vault,\n} from \"@flanksource/icons/mi\";\nimport { type StaticIconComponent } from \"../data/Icon\";\nimport { cn } from \"../lib/utils\";\nimport { UiEdit } from \"../icons\";\nimport { Combobox, type ComboboxOption } from \"./Combobox\";\nimport { IconMenuPicker, type IconMenuOption } from \"./icon-menu-picker\";\nimport { OnePasswordSelector } from \"./OnePasswordSelector\";\nimport type { OnePasswordLoaders } from \"./OnePasswordSelector.model\";\n\n// SecretKeySelector picks how a credential is sourced and lowers the choice into\n// a single reference string the consumer persists. It supports Kubernetes\n// Secrets and ConfigMaps (name + key), Helm release values (name + jsonpath\n// key), a service-account token (name only), a 1Password reference\n// (op://vault/item/field), and a static inline \"Value\". It is presentational:\n// it fetches nothing — the consumer supplies async `loadResources` /\n// `loadKeyPreview` (for keyed kinds) and optional `loadServiceAccounts` getters.\n\n/** Kinds that reference a named resource plus one of its keys. */\nexport type SecretKind = \"secret\" | \"configmap\" | \"helm\";\n\n/** Every source the picker can emit, including the non-keyed ones. */\nexport type SecretValueSource =\n | SecretKind\n | \"serviceaccount\"\n | \"onepassword\"\n | \"value\";\n\n/** One key's mid-masked preview. `value` is already masked by the consumer. */\nexport type KeyPreview = { key: string; value: string };\n\n/**\n * The selector's value: a {kind,name,key} reference into a Secret / ConfigMap /\n * Helm release, a service-account token by name, a 1Password op:// reference, or\n * a static inline literal.\n */\nexport type SecretKeyValue =\n | { kind: SecretKind; name: string; key: string }\n | { kind: \"serviceaccount\"; name: string }\n | { kind: \"onepassword\"; ref: string }\n | { kind: \"value\"; value: string };\n\n/** A named secret/configmap/helm-release and its data key names (values never returned). */\nexport type SecretResource = { name: string; keys?: string[] };\n\nexport type SecretKeySelectorProps = {\n value: SecretKeyValue | undefined;\n onChange: (next: SecretKeyValue | undefined) => void;\n /** Loads the named keyed kind's resources (name + data key names). */\n loadResources: (kind: SecretKind) => Promise<SecretResource[]>;\n /** Loads mid-masked previews for the named keyed resource's keys. */\n loadKeyPreview: (kind: SecretKind, name: string) => Promise<KeyPreview[]>;\n /**\n * Loads the service-account names (no keys) for the \"serviceaccount\" source.\n * When omitted, that source's name field accepts freeform entry only.\n */\n loadServiceAccounts?: () => Promise<SecretResource[]>;\n /** Loads metadata for the cascading 1Password vault, item, and field pickers. */\n onePassword?: OnePasswordLoaders;\n /**\n * The sources offered in the picker, in order. Defaults to\n * `[\"secret\", \"configmap\", \"value\"]` (or drops \"value\" when `allowLiteral` is\n * false). Pass an explicit list to offer helm / serviceaccount / onepassword.\n */\n sources?: SecretValueSource[];\n /**\n * Shorthand honoured only when `sources` is not supplied: `false` drops the\n * inline \"Value\" source, leaving Secret/ConfigMap. Ignored when `sources` is\n * given.\n */\n allowLiteral?: boolean;\n /**\n * When true, a chosen name absent from the loaded resources, or a key absent\n * from the chosen keyed resource's keys, is flagged invalid (once the load\n * settles). The onepassword and literal sources are never strict-flagged.\n */\n strict?: boolean;\n className?: string;\n};\n\nconst DEFAULT_SOURCES: SecretValueSource[] = [\"secret\", \"configmap\", \"value\"];\n\nconst SHRINKABLE_FIELD = \"max-w-full min-w-0 shrink\";\nconst SOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;\nconst RESOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;\nconst KEY_FIELD = `${SHRINKABLE_FIELD} basis-72 grow`;\nconst VALUE_FIELD = `${SHRINKABLE_FIELD} basis-64 grow`;\n\nexport function SecretKeySelector({\n value,\n onChange,\n loadResources,\n loadKeyPreview,\n loadServiceAccounts,\n onePassword,\n sources,\n allowLiteral = true,\n strict = false,\n className,\n}: SecretKeySelectorProps) {\n const sourceList = useMemo<SecretValueSource[]>(\n () => sources ?? (allowLiteral ? DEFAULT_SOURCES : [\"secret\", \"configmap\"]),\n [sources, allowLiteral],\n );\n\n const source: SecretValueSource = value?.kind ?? sourceList[0] ?? \"secret\";\n const isKeyed =\n source === \"secret\" || source === \"configmap\" || source === \"helm\";\n const isServiceAccount = source === \"serviceaccount\";\n const isOnePassword = source === \"onepassword\";\n const isLiteral = source === \"value\";\n // A keyed source's refKind drives the loaders; other sources default to \"secret\".\n const refKind: SecretKind = isKeyed ? (source as SecretKind) : \"secret\";\n\n const selectedName =\n value && (value.kind === \"serviceaccount\" || isRefKind(value))\n ? value.name\n : \"\";\n const selectedKey = value && isRefKind(value) ? value.key : \"\";\n const literalValue = value && value.kind === \"value\" ? value.value : \"\";\n const opRef = value && value.kind === \"onepassword\" ? value.ref : \"\";\n\n const [resources, setResources] = useState<SecretResource[]>([]);\n const [resourcesLoading, setResourcesLoading] = useState(false);\n const [previews, setPreviews] = useState<KeyPreview[]>([]);\n const [previewLoading, setPreviewLoading] = useState(false);\n\n // Load the name list for keyed kinds (secret/configmap/helm) and, when a\n // loader is supplied, for the serviceaccount source. Other sources list nothing.\n useEffect(() => {\n const loader = isKeyed\n ? () => loadResources(refKind)\n : isServiceAccount && loadServiceAccounts\n ? loadServiceAccounts\n : null;\n if (!loader) {\n setResources([]);\n return;\n }\n let cancelled = false;\n setResourcesLoading(true);\n loader()\n .then((res) => !cancelled && setResources(res))\n .finally(() => !cancelled && setResourcesLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadResources, loadServiceAccounts, refKind, isKeyed, isServiceAccount]);\n\n useEffect(() => {\n if (!isKeyed || !selectedName) {\n setPreviews([]);\n return;\n }\n let cancelled = false;\n setPreviewLoading(true);\n loadKeyPreview(refKind, selectedName)\n .then((res) => !cancelled && setPreviews(res))\n .catch(() => !cancelled && setPreviews([]))\n .finally(() => !cancelled && setPreviewLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadKeyPreview, refKind, selectedName, isKeyed]);\n\n const nameOptions = useMemo<ComboboxOption[]>(\n () => resources.map((r) => ({ value: r.name, label: r.name })),\n [resources],\n );\n const selectedResource = useMemo(\n () => resources.find((r) => r.name === selectedName),\n [resources, selectedName],\n );\n const keyOptions = useMemo(\n () => buildKeyOptions(selectedResource?.keys ?? [], previews, selectedKey),\n [selectedResource, selectedKey, previews],\n );\n\n // Strict validity: the name is invalid when it names no loaded resource; the\n // key is invalid when, with a resolved resource, it isn't one of that\n // resource's keys. Only names from a listed source are checked (a\n // serviceaccount source with no loader lists nothing, so it never flags).\n const listsNames = isKeyed || (isServiceAccount && !!loadServiceAccounts);\n const nameInvalid =\n strict &&\n listsNames &&\n !!selectedName &&\n !resourcesLoading &&\n !selectedResource;\n const keyInvalid =\n strict &&\n isKeyed &&\n !!selectedKey &&\n !!selectedResource &&\n !resourcesLoading &&\n !(selectedResource.keys ?? []).includes(selectedKey);\n\n const setSource = (next: SecretValueSource) => {\n switch (next) {\n case \"value\":\n return onChange({ kind: \"value\", value: \"\" });\n case \"onepassword\":\n return onChange({ kind: \"onepassword\", ref: \"\" });\n case \"serviceaccount\":\n return onChange({ kind: \"serviceaccount\", name: \"\" });\n default:\n return onChange({ kind: next, name: \"\", key: \"\" });\n }\n };\n const setName = (name: string) => {\n if (isServiceAccount)\n return onChange(name ? { kind: \"serviceaccount\", name } : undefined);\n return onChange(\n name ? { kind: refKind, name, key: selectedKey } : undefined,\n );\n };\n const setKey = (key: string) =>\n selectedName\n ? onChange({ kind: refKind, name: selectedName, key })\n : undefined;\n const setLiteral = (v: string) => onChange({ kind: \"value\", value: v });\n const setOpRef = (ref: string) => onChange({ kind: \"onepassword\", ref });\n\n const sourceOptions = useMemo<ComboboxOption[]>(\n () =>\n sourceList.map((k) => ({\n value: k,\n label: SOURCE_LABEL[k],\n icon: createElement(SOURCE_ICON[k], { className: \"size-4\" }),\n })),\n [sourceList],\n );\n const sourceMenuOptions = useMemo<IconMenuOption<SecretValueSource>[]>(\n () =>\n sourceList.map((kind) => ({\n value: kind,\n label: SOURCE_LABEL[kind],\n icon: SOURCE_ICON[kind],\n })),\n [sourceList],\n );\n\n return (\n <div\n data-slot=\"secret-key-selector\"\n className={cn(\"@container w-full min-w-0\", className)}\n >\n <div\n className=\"flex w-full max-w-full min-w-0 flex-nowrap items-center gap-2\"\n data-slot=\"secret-fields\"\n >\n <div\n className={cn(SOURCE_FIELD, \"@max-md:hidden\")}\n data-slot=\"secret-source-combobox\"\n >\n <Combobox\n ariaLabel=\"Secret value source\"\n options={sourceOptions}\n value={source}\n onChange={(next) => setSource(next as SecretValueSource)}\n allowCustomValue={false}\n required\n />\n </div>\n <div\n className=\"hidden shrink-0 @max-md:!block\"\n data-slot=\"secret-source-menu\"\n >\n <IconMenuPicker\n value={source}\n onChange={setSource}\n options={sourceMenuOptions}\n ariaLabel=\"Secret value source\"\n triggerClassName=\"size-control-h rounded-md border border-input bg-background\"\n />\n </div>\n {isLiteral ? (\n <TextField\n value={literalValue}\n onChange={setLiteral}\n placeholder=\"Static value…\"\n ariaLabel=\"Static value\"\n />\n ) : isOnePassword ? (\n (() => {\n if (!onePassword) {\n throw new Error(\n \"SecretKeySelector onepassword source requires onePassword loaders\",\n );\n }\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-onepassword-field\">\n <OnePasswordSelector\n value={opRef}\n onChange={setOpRef}\n {...onePassword}\n allowCustomValue\n />\n </div>\n );\n })()\n ) : isServiceAccount ? (\n <div\n className={VALUE_FIELD}\n data-slot=\"secret-serviceaccount-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder=\"Service account…\"\n />\n </div>\n ) : (\n <div\n className={cn(\n \"flex flex-nowrap items-center gap-2\",\n SHRINKABLE_FIELD,\n \"grow\",\n )}\n data-slot=\"secret-reference-fields\"\n >\n <div\n className={RESOURCE_FIELD}\n data-slot=\"secret-resource-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder={`Select ${refKind}…`}\n />\n </div>\n <div className={KEY_FIELD} data-slot=\"secret-key-field\">\n <Combobox\n options={keyOptions}\n value={selectedKey}\n onChange={setKey}\n allowCustomValue\n loading={previewLoading}\n invalid={keyInvalid}\n placeholder={selectedName ? keyPlaceholder(refKind) : \"—\"}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction TextField({\n value,\n onChange,\n placeholder,\n ariaLabel,\n}: {\n value: string;\n onChange: (v: string) => void;\n placeholder: string;\n ariaLabel: string;\n}) {\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-value-fields\">\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n className={cn(\n \"h-control-h w-full min-w-0 rounded border border-input bg-background px-control-px text-sm\",\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n />\n </div>\n );\n}\n\n// isRefKind narrows to the keyed variants that carry both a name and a key.\nfunction isRefKind(\n v: SecretKeyValue,\n): v is { kind: SecretKind; name: string; key: string } {\n return v.kind === \"secret\" || v.kind === \"configmap\" || v.kind === \"helm\";\n}\n\n// keyPlaceholder hints the key field: helm keys are jsonpath expressions into\n// the merged release values, the others are literal data keys.\nfunction keyPlaceholder(kind: SecretKind): string {\n return kind === \"helm\" ? \"jsonpath…\" : \"Key…\";\n}\n\n// Each source's leading glyph. The flanksource FCs carry static metadata that\n// doesn't unify with ComponentType, so type the slot as a plain render function.\nconst SOURCE_ICON: Record<SecretValueSource, StaticIconComponent> = {\n secret: K8SSecret,\n configmap: K8SConfigmap,\n helm: Helm,\n serviceaccount: K8SServiceaccount,\n onepassword: Vault,\n value: UiEdit,\n};\n\nconst SOURCE_LABEL: Record<SecretValueSource, string> = {\n secret: \"Secret\",\n configmap: \"ConfigMap\",\n helm: \"Helm\",\n serviceaccount: \"Service Account\",\n onepassword: \"1Password\",\n value: \"Value\",\n};\n\n// buildKeyOptions labels each key with its mid-masked preview value when one is\n// available. A selected key absent from the list is pinned so the selection\n// stays visible.\nfunction buildKeyOptions(\n keys: string[],\n previews: KeyPreview[],\n selectedKey: string,\n): ComboboxOption[] {\n const masked = new Map(previews.map((p) => [p.key, p.value]));\n const base = keys.map((k) => ({\n value: k,\n label: masked.has(k) ? `${k} — ${masked.get(k)}` : k,\n }));\n if (selectedKey && !base.some((o) => o.value === selectedKey)) {\n return [{ value: selectedKey, label: selectedKey }, ...base];\n }\n return base;\n}\n"],"names":["useMemo","useState","useEffect","createElement","jsx","cn","jsxs","Combobox","IconMenuPicker","OnePasswordSelector","K8SSecret","K8SConfigmap","Helm","K8SServiceaccount","Vault","UiEdit"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SecretKeySelector.cjs","sources":["../../src/components/SecretKeySelector.tsx"],"sourcesContent":["import { createElement, useEffect, useMemo, useState } from \"react\";\nimport {\n K8SSecret,\n K8SConfigmap,\n Helm,\n K8SServiceaccount,\n Vault,\n} from \"@flanksource/icons/mi\";\nimport { type StaticIconComponent } from \"../data/Icon\";\nimport { cn } from \"../lib/utils\";\nimport { UiEdit } from \"../icons\";\nimport { Combobox, type ComboboxOption } from \"./Combobox\";\nimport { IconMenuPicker, type IconMenuOption } from \"./icon-menu-picker\";\nimport { OnePasswordSelector } from \"./OnePasswordSelector\";\nimport type { OnePasswordLoaders } from \"./OnePasswordSelector.model\";\n\n// SecretKeySelector picks how a credential is sourced and lowers the choice into\n// a single reference string the consumer persists. It supports Kubernetes\n// Secrets and ConfigMaps (name + key), Helm release values (name + jsonpath\n// key), a service-account token (name only), a 1Password reference\n// (op://vault/item/field), and a static inline \"Value\". It is presentational:\n// it fetches nothing — the consumer supplies async `loadResources` /\n// `loadKeyPreview` (for keyed kinds) and optional `loadServiceAccounts` getters.\n\n/** Kinds that reference a named resource plus one of its keys. */\nexport type SecretKind = \"secret\" | \"configmap\" | \"helm\";\n\n/** Every source the picker can emit, including the non-keyed ones. */\nexport type SecretValueSource =\n | SecretKind\n | \"serviceaccount\"\n | \"onepassword\"\n | \"value\";\n\n/** One key's mid-masked preview. `value` is already masked by the consumer. */\nexport type KeyPreview = { key: string; value: string };\n\n/**\n * The selector's value: a {kind,name,key} reference into a Secret / ConfigMap /\n * Helm release, a service-account token by name, a 1Password op:// reference, or\n * a static inline literal.\n */\nexport type SecretKeyValue =\n | { kind: SecretKind; name: string; key: string }\n | { kind: \"serviceaccount\"; name: string }\n | { kind: \"onepassword\"; ref: string }\n | { kind: \"value\"; value: string };\n\n/** A named secret/configmap/helm-release and its data key names (values never returned). */\nexport type SecretResource = { name: string; keys?: string[] };\n\nexport type SecretKeySelectorProps = {\n value: SecretKeyValue | undefined;\n onChange: (next: SecretKeyValue | undefined) => void;\n /** Loads the named keyed kind's resources (name + data key names). */\n loadResources: (kind: SecretKind) => Promise<SecretResource[]>;\n /** Loads mid-masked previews for the named keyed resource's keys. */\n loadKeyPreview: (kind: SecretKind, name: string) => Promise<KeyPreview[]>;\n /**\n * Loads the service-account names (no keys) for the \"serviceaccount\" source.\n * When omitted, that source's name field accepts freeform entry only.\n */\n loadServiceAccounts?: () => Promise<SecretResource[]>;\n /** Loads metadata for the cascading 1Password vault, item, and field pickers. */\n onePassword?: OnePasswordLoaders;\n /**\n * The sources offered in the picker, in order. Defaults to\n * `[\"secret\", \"configmap\", \"value\"]` (or drops \"value\" when `allowLiteral` is\n * false). Pass an explicit list to offer helm / serviceaccount / onepassword.\n */\n sources?: SecretValueSource[];\n /**\n * Shorthand honoured only when `sources` is not supplied: `false` drops the\n * inline \"Value\" source, leaving Secret/ConfigMap. Ignored when `sources` is\n * given.\n */\n allowLiteral?: boolean;\n /**\n * When true, a chosen name absent from the loaded resources, or a key absent\n * from the chosen keyed resource's keys, is flagged invalid (once the load\n * settles). The onepassword and literal sources are never strict-flagged.\n */\n strict?: boolean;\n className?: string;\n};\n\nconst DEFAULT_SOURCES: SecretValueSource[] = [\"secret\", \"configmap\", \"value\"];\n\nconst SHRINKABLE_FIELD = \"max-w-full min-w-0 shrink\";\nconst SOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;\nconst RESOURCE_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow-0 @min-[22rem]/secret:basis-40`;\nconst KEY_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow @min-[22rem]/secret:basis-72`;\nconst VALUE_FIELD = `${SHRINKABLE_FIELD} basis-64 grow`;\n\nexport function SecretKeySelector({\n value,\n onChange,\n loadResources,\n loadKeyPreview,\n loadServiceAccounts,\n onePassword,\n sources,\n allowLiteral = true,\n strict = false,\n className,\n}: SecretKeySelectorProps) {\n const sourceList = useMemo<SecretValueSource[]>(\n () => sources ?? (allowLiteral ? DEFAULT_SOURCES : [\"secret\", \"configmap\"]),\n [sources, allowLiteral],\n );\n\n const source: SecretValueSource = value?.kind ?? sourceList[0] ?? \"secret\";\n const isKeyed =\n source === \"secret\" || source === \"configmap\" || source === \"helm\";\n const isServiceAccount = source === \"serviceaccount\";\n const isOnePassword = source === \"onepassword\";\n const isLiteral = source === \"value\";\n // A keyed source's refKind drives the loaders; other sources default to \"secret\".\n const refKind: SecretKind = isKeyed ? (source as SecretKind) : \"secret\";\n\n const selectedName =\n value && (value.kind === \"serviceaccount\" || isRefKind(value))\n ? value.name\n : \"\";\n const selectedKey = value && isRefKind(value) ? value.key : \"\";\n const literalValue = value && value.kind === \"value\" ? value.value : \"\";\n const opRef = value && value.kind === \"onepassword\" ? value.ref : \"\";\n\n const [resources, setResources] = useState<SecretResource[]>([]);\n const [resourcesLoading, setResourcesLoading] = useState(false);\n const [previews, setPreviews] = useState<KeyPreview[]>([]);\n const [previewLoading, setPreviewLoading] = useState(false);\n\n // Load the name list for keyed kinds (secret/configmap/helm) and, when a\n // loader is supplied, for the serviceaccount source. Other sources list nothing.\n useEffect(() => {\n const loader = isKeyed\n ? () => loadResources(refKind)\n : isServiceAccount && loadServiceAccounts\n ? loadServiceAccounts\n : null;\n if (!loader) {\n setResources([]);\n return;\n }\n let cancelled = false;\n setResourcesLoading(true);\n loader()\n .then((res) => !cancelled && setResources(res))\n .finally(() => !cancelled && setResourcesLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadResources, loadServiceAccounts, refKind, isKeyed, isServiceAccount]);\n\n useEffect(() => {\n if (!isKeyed || !selectedName) {\n setPreviews([]);\n return;\n }\n let cancelled = false;\n setPreviewLoading(true);\n loadKeyPreview(refKind, selectedName)\n .then((res) => !cancelled && setPreviews(res))\n .catch(() => !cancelled && setPreviews([]))\n .finally(() => !cancelled && setPreviewLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadKeyPreview, refKind, selectedName, isKeyed]);\n\n const nameOptions = useMemo<ComboboxOption[]>(\n () => resources.map((r) => ({ value: r.name, label: r.name })),\n [resources],\n );\n const selectedResource = useMemo(\n () => resources.find((r) => r.name === selectedName),\n [resources, selectedName],\n );\n const keyOptions = useMemo(\n () => buildKeyOptions(selectedResource?.keys ?? [], previews, selectedKey),\n [selectedResource, selectedKey, previews],\n );\n\n // Strict validity: the name is invalid when it names no loaded resource; the\n // key is invalid when, with a resolved resource, it isn't one of that\n // resource's keys. Only names from a listed source are checked (a\n // serviceaccount source with no loader lists nothing, so it never flags).\n const listsNames = isKeyed || (isServiceAccount && !!loadServiceAccounts);\n const nameInvalid =\n strict &&\n listsNames &&\n !!selectedName &&\n !resourcesLoading &&\n !selectedResource;\n const keyInvalid =\n strict &&\n isKeyed &&\n !!selectedKey &&\n !!selectedResource &&\n !resourcesLoading &&\n !(selectedResource.keys ?? []).includes(selectedKey);\n\n const setSource = (next: SecretValueSource) => {\n switch (next) {\n case \"value\":\n return onChange({ kind: \"value\", value: \"\" });\n case \"onepassword\":\n return onChange({ kind: \"onepassword\", ref: \"\" });\n case \"serviceaccount\":\n return onChange({ kind: \"serviceaccount\", name: \"\" });\n default:\n return onChange({ kind: next, name: \"\", key: \"\" });\n }\n };\n const setName = (name: string) => {\n if (isServiceAccount)\n return onChange(name ? { kind: \"serviceaccount\", name } : undefined);\n return onChange(\n name ? { kind: refKind, name, key: selectedKey } : undefined,\n );\n };\n const setKey = (key: string) =>\n selectedName\n ? onChange({ kind: refKind, name: selectedName, key })\n : undefined;\n const setLiteral = (v: string) => onChange({ kind: \"value\", value: v });\n const setOpRef = (ref: string) => onChange({ kind: \"onepassword\", ref });\n\n const sourceOptions = useMemo<ComboboxOption[]>(\n () =>\n sourceList.map((k) => ({\n value: k,\n label: SOURCE_LABEL[k],\n icon: createElement(SOURCE_ICON[k], { className: \"size-4\" }),\n })),\n [sourceList],\n );\n const sourceMenuOptions = useMemo<IconMenuOption<SecretValueSource>[]>(\n () =>\n sourceList.map((kind) => ({\n value: kind,\n label: SOURCE_LABEL[kind],\n icon: SOURCE_ICON[kind],\n })),\n [sourceList],\n );\n\n return (\n <div\n data-slot=\"secret-key-selector\"\n className={cn(\"@container/secret w-full min-w-0\", className)}\n >\n <div\n className=\"flex w-full max-w-full min-w-0 flex-nowrap items-center gap-2\"\n data-slot=\"secret-fields\"\n >\n <div\n className={cn(SOURCE_FIELD, \"@max-md:hidden\")}\n data-slot=\"secret-source-combobox\"\n >\n <Combobox\n ariaLabel=\"Secret value source\"\n options={sourceOptions}\n value={source}\n onChange={(next) => setSource(next as SecretValueSource)}\n allowCustomValue={false}\n required\n />\n </div>\n <div\n className=\"hidden shrink-0 @max-md:!block\"\n data-slot=\"secret-source-menu\"\n >\n <IconMenuPicker\n value={source}\n onChange={setSource}\n options={sourceMenuOptions}\n ariaLabel=\"Secret value source\"\n triggerClassName=\"size-control-h rounded-md border border-input bg-background\"\n />\n </div>\n {isLiteral ? (\n <TextField\n value={literalValue}\n onChange={setLiteral}\n placeholder=\"Static value…\"\n ariaLabel=\"Static value\"\n />\n ) : isOnePassword ? (\n (() => {\n if (!onePassword) {\n throw new Error(\n \"SecretKeySelector onepassword source requires onePassword loaders\",\n );\n }\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-onepassword-field\">\n <OnePasswordSelector\n value={opRef}\n onChange={setOpRef}\n {...onePassword}\n allowCustomValue\n />\n </div>\n );\n })()\n ) : isServiceAccount ? (\n <div\n className={VALUE_FIELD}\n data-slot=\"secret-serviceaccount-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder=\"Service account…\"\n />\n </div>\n ) : (\n <div\n className={cn(\n \"flex flex-col items-center gap-2 @min-[22rem]/secret:flex-row\",\n SHRINKABLE_FIELD,\n \"grow\",\n )}\n data-slot=\"secret-reference-fields\"\n >\n <div\n className={RESOURCE_FIELD}\n data-slot=\"secret-resource-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder={`Select ${refKind}…`}\n />\n </div>\n <div className={KEY_FIELD} data-slot=\"secret-key-field\">\n <Combobox\n options={keyOptions}\n value={selectedKey}\n onChange={setKey}\n allowCustomValue\n loading={previewLoading}\n invalid={keyInvalid}\n placeholder={selectedName ? keyPlaceholder(refKind) : \"—\"}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction TextField({\n value,\n onChange,\n placeholder,\n ariaLabel,\n}: {\n value: string;\n onChange: (v: string) => void;\n placeholder: string;\n ariaLabel: string;\n}) {\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-value-fields\">\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n className={cn(\n \"h-control-h w-full min-w-0 rounded border border-input bg-background px-control-px text-sm\",\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n />\n </div>\n );\n}\n\n// isRefKind narrows to the keyed variants that carry both a name and a key.\nfunction isRefKind(\n v: SecretKeyValue,\n): v is { kind: SecretKind; name: string; key: string } {\n return v.kind === \"secret\" || v.kind === \"configmap\" || v.kind === \"helm\";\n}\n\n// keyPlaceholder hints the key field: helm keys are jsonpath expressions into\n// the merged release values, the others are literal data keys.\nfunction keyPlaceholder(kind: SecretKind): string {\n return kind === \"helm\" ? \"jsonpath…\" : \"Key…\";\n}\n\n// Each source's leading glyph. The flanksource FCs carry static metadata that\n// doesn't unify with ComponentType, so type the slot as a plain render function.\nconst SOURCE_ICON: Record<SecretValueSource, StaticIconComponent> = {\n secret: K8SSecret,\n configmap: K8SConfigmap,\n helm: Helm,\n serviceaccount: K8SServiceaccount,\n onepassword: Vault,\n value: UiEdit,\n};\n\nconst SOURCE_LABEL: Record<SecretValueSource, string> = {\n secret: \"Secret\",\n configmap: \"ConfigMap\",\n helm: \"Helm\",\n serviceaccount: \"Service Account\",\n onepassword: \"1Password\",\n value: \"Value\",\n};\n\n// buildKeyOptions labels each key with its mid-masked preview value when one is\n// available. A selected key absent from the list is pinned so the selection\n// stays visible.\nfunction buildKeyOptions(\n keys: string[],\n previews: KeyPreview[],\n selectedKey: string,\n): ComboboxOption[] {\n const masked = new Map(previews.map((p) => [p.key, p.value]));\n const base = keys.map((k) => ({\n value: k,\n label: masked.has(k) ? `${k} — ${masked.get(k)}` : k,\n }));\n if (selectedKey && !base.some((o) => o.value === selectedKey)) {\n return [{ value: selectedKey, label: selectedKey }, ...base];\n }\n return base;\n}\n"],"names":["useMemo","useState","useEffect","createElement","jsx","cn","jsxs","Combobox","IconMenuPicker","OnePasswordSelector","K8SSecret","K8SConfigmap","Helm","K8SServiceaccount","Vault","UiEdit"],"mappings":"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"}
|
|
@@ -9,8 +9,8 @@ import { UiEdit } from "../icons/components/UiEdit.js";
|
|
|
9
9
|
const DEFAULT_SOURCES = ["secret", "configmap", "value"];
|
|
10
10
|
const SHRINKABLE_FIELD = "max-w-full min-w-0 shrink";
|
|
11
11
|
const SOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;
|
|
12
|
-
const RESOURCE_FIELD = `${SHRINKABLE_FIELD} basis-
|
|
13
|
-
const KEY_FIELD = `${SHRINKABLE_FIELD} basis-
|
|
12
|
+
const RESOURCE_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow-0 @min-[22rem]/secret:basis-40`;
|
|
13
|
+
const KEY_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow @min-[22rem]/secret:basis-72`;
|
|
14
14
|
const VALUE_FIELD = `${SHRINKABLE_FIELD} basis-64 grow`;
|
|
15
15
|
function SecretKeySelector({
|
|
16
16
|
value,
|
|
@@ -124,7 +124,7 @@ function SecretKeySelector({
|
|
|
124
124
|
"div",
|
|
125
125
|
{
|
|
126
126
|
"data-slot": "secret-key-selector",
|
|
127
|
-
className: cn("@container w-full min-w-0", className),
|
|
127
|
+
className: cn("@container/secret w-full min-w-0", className),
|
|
128
128
|
children: /* @__PURE__ */ jsxs(
|
|
129
129
|
"div",
|
|
130
130
|
{
|
|
@@ -211,7 +211,7 @@ function SecretKeySelector({
|
|
|
211
211
|
"div",
|
|
212
212
|
{
|
|
213
213
|
className: cn(
|
|
214
|
-
"flex flex-
|
|
214
|
+
"flex flex-col items-center gap-2 @min-[22rem]/secret:flex-row",
|
|
215
215
|
SHRINKABLE_FIELD,
|
|
216
216
|
"grow"
|
|
217
217
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretKeySelector.js","sources":["../../src/components/SecretKeySelector.tsx"],"sourcesContent":["import { createElement, useEffect, useMemo, useState } from \"react\";\nimport {\n K8SSecret,\n K8SConfigmap,\n Helm,\n K8SServiceaccount,\n Vault,\n} from \"@flanksource/icons/mi\";\nimport { type StaticIconComponent } from \"../data/Icon\";\nimport { cn } from \"../lib/utils\";\nimport { UiEdit } from \"../icons\";\nimport { Combobox, type ComboboxOption } from \"./Combobox\";\nimport { IconMenuPicker, type IconMenuOption } from \"./icon-menu-picker\";\nimport { OnePasswordSelector } from \"./OnePasswordSelector\";\nimport type { OnePasswordLoaders } from \"./OnePasswordSelector.model\";\n\n// SecretKeySelector picks how a credential is sourced and lowers the choice into\n// a single reference string the consumer persists. It supports Kubernetes\n// Secrets and ConfigMaps (name + key), Helm release values (name + jsonpath\n// key), a service-account token (name only), a 1Password reference\n// (op://vault/item/field), and a static inline \"Value\". It is presentational:\n// it fetches nothing — the consumer supplies async `loadResources` /\n// `loadKeyPreview` (for keyed kinds) and optional `loadServiceAccounts` getters.\n\n/** Kinds that reference a named resource plus one of its keys. */\nexport type SecretKind = \"secret\" | \"configmap\" | \"helm\";\n\n/** Every source the picker can emit, including the non-keyed ones. */\nexport type SecretValueSource =\n | SecretKind\n | \"serviceaccount\"\n | \"onepassword\"\n | \"value\";\n\n/** One key's mid-masked preview. `value` is already masked by the consumer. */\nexport type KeyPreview = { key: string; value: string };\n\n/**\n * The selector's value: a {kind,name,key} reference into a Secret / ConfigMap /\n * Helm release, a service-account token by name, a 1Password op:// reference, or\n * a static inline literal.\n */\nexport type SecretKeyValue =\n | { kind: SecretKind; name: string; key: string }\n | { kind: \"serviceaccount\"; name: string }\n | { kind: \"onepassword\"; ref: string }\n | { kind: \"value\"; value: string };\n\n/** A named secret/configmap/helm-release and its data key names (values never returned). */\nexport type SecretResource = { name: string; keys?: string[] };\n\nexport type SecretKeySelectorProps = {\n value: SecretKeyValue | undefined;\n onChange: (next: SecretKeyValue | undefined) => void;\n /** Loads the named keyed kind's resources (name + data key names). */\n loadResources: (kind: SecretKind) => Promise<SecretResource[]>;\n /** Loads mid-masked previews for the named keyed resource's keys. */\n loadKeyPreview: (kind: SecretKind, name: string) => Promise<KeyPreview[]>;\n /**\n * Loads the service-account names (no keys) for the \"serviceaccount\" source.\n * When omitted, that source's name field accepts freeform entry only.\n */\n loadServiceAccounts?: () => Promise<SecretResource[]>;\n /** Loads metadata for the cascading 1Password vault, item, and field pickers. */\n onePassword?: OnePasswordLoaders;\n /**\n * The sources offered in the picker, in order. Defaults to\n * `[\"secret\", \"configmap\", \"value\"]` (or drops \"value\" when `allowLiteral` is\n * false). Pass an explicit list to offer helm / serviceaccount / onepassword.\n */\n sources?: SecretValueSource[];\n /**\n * Shorthand honoured only when `sources` is not supplied: `false` drops the\n * inline \"Value\" source, leaving Secret/ConfigMap. Ignored when `sources` is\n * given.\n */\n allowLiteral?: boolean;\n /**\n * When true, a chosen name absent from the loaded resources, or a key absent\n * from the chosen keyed resource's keys, is flagged invalid (once the load\n * settles). The onepassword and literal sources are never strict-flagged.\n */\n strict?: boolean;\n className?: string;\n};\n\nconst DEFAULT_SOURCES: SecretValueSource[] = [\"secret\", \"configmap\", \"value\"];\n\nconst SHRINKABLE_FIELD = \"max-w-full min-w-0 shrink\";\nconst SOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;\nconst RESOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;\nconst KEY_FIELD = `${SHRINKABLE_FIELD} basis-72 grow`;\nconst VALUE_FIELD = `${SHRINKABLE_FIELD} basis-64 grow`;\n\nexport function SecretKeySelector({\n value,\n onChange,\n loadResources,\n loadKeyPreview,\n loadServiceAccounts,\n onePassword,\n sources,\n allowLiteral = true,\n strict = false,\n className,\n}: SecretKeySelectorProps) {\n const sourceList = useMemo<SecretValueSource[]>(\n () => sources ?? (allowLiteral ? DEFAULT_SOURCES : [\"secret\", \"configmap\"]),\n [sources, allowLiteral],\n );\n\n const source: SecretValueSource = value?.kind ?? sourceList[0] ?? \"secret\";\n const isKeyed =\n source === \"secret\" || source === \"configmap\" || source === \"helm\";\n const isServiceAccount = source === \"serviceaccount\";\n const isOnePassword = source === \"onepassword\";\n const isLiteral = source === \"value\";\n // A keyed source's refKind drives the loaders; other sources default to \"secret\".\n const refKind: SecretKind = isKeyed ? (source as SecretKind) : \"secret\";\n\n const selectedName =\n value && (value.kind === \"serviceaccount\" || isRefKind(value))\n ? value.name\n : \"\";\n const selectedKey = value && isRefKind(value) ? value.key : \"\";\n const literalValue = value && value.kind === \"value\" ? value.value : \"\";\n const opRef = value && value.kind === \"onepassword\" ? value.ref : \"\";\n\n const [resources, setResources] = useState<SecretResource[]>([]);\n const [resourcesLoading, setResourcesLoading] = useState(false);\n const [previews, setPreviews] = useState<KeyPreview[]>([]);\n const [previewLoading, setPreviewLoading] = useState(false);\n\n // Load the name list for keyed kinds (secret/configmap/helm) and, when a\n // loader is supplied, for the serviceaccount source. Other sources list nothing.\n useEffect(() => {\n const loader = isKeyed\n ? () => loadResources(refKind)\n : isServiceAccount && loadServiceAccounts\n ? loadServiceAccounts\n : null;\n if (!loader) {\n setResources([]);\n return;\n }\n let cancelled = false;\n setResourcesLoading(true);\n loader()\n .then((res) => !cancelled && setResources(res))\n .finally(() => !cancelled && setResourcesLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadResources, loadServiceAccounts, refKind, isKeyed, isServiceAccount]);\n\n useEffect(() => {\n if (!isKeyed || !selectedName) {\n setPreviews([]);\n return;\n }\n let cancelled = false;\n setPreviewLoading(true);\n loadKeyPreview(refKind, selectedName)\n .then((res) => !cancelled && setPreviews(res))\n .catch(() => !cancelled && setPreviews([]))\n .finally(() => !cancelled && setPreviewLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadKeyPreview, refKind, selectedName, isKeyed]);\n\n const nameOptions = useMemo<ComboboxOption[]>(\n () => resources.map((r) => ({ value: r.name, label: r.name })),\n [resources],\n );\n const selectedResource = useMemo(\n () => resources.find((r) => r.name === selectedName),\n [resources, selectedName],\n );\n const keyOptions = useMemo(\n () => buildKeyOptions(selectedResource?.keys ?? [], previews, selectedKey),\n [selectedResource, selectedKey, previews],\n );\n\n // Strict validity: the name is invalid when it names no loaded resource; the\n // key is invalid when, with a resolved resource, it isn't one of that\n // resource's keys. Only names from a listed source are checked (a\n // serviceaccount source with no loader lists nothing, so it never flags).\n const listsNames = isKeyed || (isServiceAccount && !!loadServiceAccounts);\n const nameInvalid =\n strict &&\n listsNames &&\n !!selectedName &&\n !resourcesLoading &&\n !selectedResource;\n const keyInvalid =\n strict &&\n isKeyed &&\n !!selectedKey &&\n !!selectedResource &&\n !resourcesLoading &&\n !(selectedResource.keys ?? []).includes(selectedKey);\n\n const setSource = (next: SecretValueSource) => {\n switch (next) {\n case \"value\":\n return onChange({ kind: \"value\", value: \"\" });\n case \"onepassword\":\n return onChange({ kind: \"onepassword\", ref: \"\" });\n case \"serviceaccount\":\n return onChange({ kind: \"serviceaccount\", name: \"\" });\n default:\n return onChange({ kind: next, name: \"\", key: \"\" });\n }\n };\n const setName = (name: string) => {\n if (isServiceAccount)\n return onChange(name ? { kind: \"serviceaccount\", name } : undefined);\n return onChange(\n name ? { kind: refKind, name, key: selectedKey } : undefined,\n );\n };\n const setKey = (key: string) =>\n selectedName\n ? onChange({ kind: refKind, name: selectedName, key })\n : undefined;\n const setLiteral = (v: string) => onChange({ kind: \"value\", value: v });\n const setOpRef = (ref: string) => onChange({ kind: \"onepassword\", ref });\n\n const sourceOptions = useMemo<ComboboxOption[]>(\n () =>\n sourceList.map((k) => ({\n value: k,\n label: SOURCE_LABEL[k],\n icon: createElement(SOURCE_ICON[k], { className: \"size-4\" }),\n })),\n [sourceList],\n );\n const sourceMenuOptions = useMemo<IconMenuOption<SecretValueSource>[]>(\n () =>\n sourceList.map((kind) => ({\n value: kind,\n label: SOURCE_LABEL[kind],\n icon: SOURCE_ICON[kind],\n })),\n [sourceList],\n );\n\n return (\n <div\n data-slot=\"secret-key-selector\"\n className={cn(\"@container w-full min-w-0\", className)}\n >\n <div\n className=\"flex w-full max-w-full min-w-0 flex-nowrap items-center gap-2\"\n data-slot=\"secret-fields\"\n >\n <div\n className={cn(SOURCE_FIELD, \"@max-md:hidden\")}\n data-slot=\"secret-source-combobox\"\n >\n <Combobox\n ariaLabel=\"Secret value source\"\n options={sourceOptions}\n value={source}\n onChange={(next) => setSource(next as SecretValueSource)}\n allowCustomValue={false}\n required\n />\n </div>\n <div\n className=\"hidden shrink-0 @max-md:!block\"\n data-slot=\"secret-source-menu\"\n >\n <IconMenuPicker\n value={source}\n onChange={setSource}\n options={sourceMenuOptions}\n ariaLabel=\"Secret value source\"\n triggerClassName=\"size-control-h rounded-md border border-input bg-background\"\n />\n </div>\n {isLiteral ? (\n <TextField\n value={literalValue}\n onChange={setLiteral}\n placeholder=\"Static value…\"\n ariaLabel=\"Static value\"\n />\n ) : isOnePassword ? (\n (() => {\n if (!onePassword) {\n throw new Error(\n \"SecretKeySelector onepassword source requires onePassword loaders\",\n );\n }\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-onepassword-field\">\n <OnePasswordSelector\n value={opRef}\n onChange={setOpRef}\n {...onePassword}\n allowCustomValue\n />\n </div>\n );\n })()\n ) : isServiceAccount ? (\n <div\n className={VALUE_FIELD}\n data-slot=\"secret-serviceaccount-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder=\"Service account…\"\n />\n </div>\n ) : (\n <div\n className={cn(\n \"flex flex-nowrap items-center gap-2\",\n SHRINKABLE_FIELD,\n \"grow\",\n )}\n data-slot=\"secret-reference-fields\"\n >\n <div\n className={RESOURCE_FIELD}\n data-slot=\"secret-resource-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder={`Select ${refKind}…`}\n />\n </div>\n <div className={KEY_FIELD} data-slot=\"secret-key-field\">\n <Combobox\n options={keyOptions}\n value={selectedKey}\n onChange={setKey}\n allowCustomValue\n loading={previewLoading}\n invalid={keyInvalid}\n placeholder={selectedName ? keyPlaceholder(refKind) : \"—\"}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction TextField({\n value,\n onChange,\n placeholder,\n ariaLabel,\n}: {\n value: string;\n onChange: (v: string) => void;\n placeholder: string;\n ariaLabel: string;\n}) {\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-value-fields\">\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n className={cn(\n \"h-control-h w-full min-w-0 rounded border border-input bg-background px-control-px text-sm\",\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n />\n </div>\n );\n}\n\n// isRefKind narrows to the keyed variants that carry both a name and a key.\nfunction isRefKind(\n v: SecretKeyValue,\n): v is { kind: SecretKind; name: string; key: string } {\n return v.kind === \"secret\" || v.kind === \"configmap\" || v.kind === \"helm\";\n}\n\n// keyPlaceholder hints the key field: helm keys are jsonpath expressions into\n// the merged release values, the others are literal data keys.\nfunction keyPlaceholder(kind: SecretKind): string {\n return kind === \"helm\" ? \"jsonpath…\" : \"Key…\";\n}\n\n// Each source's leading glyph. The flanksource FCs carry static metadata that\n// doesn't unify with ComponentType, so type the slot as a plain render function.\nconst SOURCE_ICON: Record<SecretValueSource, StaticIconComponent> = {\n secret: K8SSecret,\n configmap: K8SConfigmap,\n helm: Helm,\n serviceaccount: K8SServiceaccount,\n onepassword: Vault,\n value: UiEdit,\n};\n\nconst SOURCE_LABEL: Record<SecretValueSource, string> = {\n secret: \"Secret\",\n configmap: \"ConfigMap\",\n helm: \"Helm\",\n serviceaccount: \"Service Account\",\n onepassword: \"1Password\",\n value: \"Value\",\n};\n\n// buildKeyOptions labels each key with its mid-masked preview value when one is\n// available. A selected key absent from the list is pinned so the selection\n// stays visible.\nfunction buildKeyOptions(\n keys: string[],\n previews: KeyPreview[],\n selectedKey: string,\n): ComboboxOption[] {\n const masked = new Map(previews.map((p) => [p.key, p.value]));\n const base = keys.map((k) => ({\n value: k,\n label: masked.has(k) ? `${k} — ${masked.get(k)}` : k,\n }));\n if (selectedKey && !base.some((o) => o.value === selectedKey)) {\n return [{ value: selectedKey, label: selectedKey }, ...base];\n }\n return base;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SecretKeySelector.js","sources":["../../src/components/SecretKeySelector.tsx"],"sourcesContent":["import { createElement, useEffect, useMemo, useState } from \"react\";\nimport {\n K8SSecret,\n K8SConfigmap,\n Helm,\n K8SServiceaccount,\n Vault,\n} from \"@flanksource/icons/mi\";\nimport { type StaticIconComponent } from \"../data/Icon\";\nimport { cn } from \"../lib/utils\";\nimport { UiEdit } from \"../icons\";\nimport { Combobox, type ComboboxOption } from \"./Combobox\";\nimport { IconMenuPicker, type IconMenuOption } from \"./icon-menu-picker\";\nimport { OnePasswordSelector } from \"./OnePasswordSelector\";\nimport type { OnePasswordLoaders } from \"./OnePasswordSelector.model\";\n\n// SecretKeySelector picks how a credential is sourced and lowers the choice into\n// a single reference string the consumer persists. It supports Kubernetes\n// Secrets and ConfigMaps (name + key), Helm release values (name + jsonpath\n// key), a service-account token (name only), a 1Password reference\n// (op://vault/item/field), and a static inline \"Value\". It is presentational:\n// it fetches nothing — the consumer supplies async `loadResources` /\n// `loadKeyPreview` (for keyed kinds) and optional `loadServiceAccounts` getters.\n\n/** Kinds that reference a named resource plus one of its keys. */\nexport type SecretKind = \"secret\" | \"configmap\" | \"helm\";\n\n/** Every source the picker can emit, including the non-keyed ones. */\nexport type SecretValueSource =\n | SecretKind\n | \"serviceaccount\"\n | \"onepassword\"\n | \"value\";\n\n/** One key's mid-masked preview. `value` is already masked by the consumer. */\nexport type KeyPreview = { key: string; value: string };\n\n/**\n * The selector's value: a {kind,name,key} reference into a Secret / ConfigMap /\n * Helm release, a service-account token by name, a 1Password op:// reference, or\n * a static inline literal.\n */\nexport type SecretKeyValue =\n | { kind: SecretKind; name: string; key: string }\n | { kind: \"serviceaccount\"; name: string }\n | { kind: \"onepassword\"; ref: string }\n | { kind: \"value\"; value: string };\n\n/** A named secret/configmap/helm-release and its data key names (values never returned). */\nexport type SecretResource = { name: string; keys?: string[] };\n\nexport type SecretKeySelectorProps = {\n value: SecretKeyValue | undefined;\n onChange: (next: SecretKeyValue | undefined) => void;\n /** Loads the named keyed kind's resources (name + data key names). */\n loadResources: (kind: SecretKind) => Promise<SecretResource[]>;\n /** Loads mid-masked previews for the named keyed resource's keys. */\n loadKeyPreview: (kind: SecretKind, name: string) => Promise<KeyPreview[]>;\n /**\n * Loads the service-account names (no keys) for the \"serviceaccount\" source.\n * When omitted, that source's name field accepts freeform entry only.\n */\n loadServiceAccounts?: () => Promise<SecretResource[]>;\n /** Loads metadata for the cascading 1Password vault, item, and field pickers. */\n onePassword?: OnePasswordLoaders;\n /**\n * The sources offered in the picker, in order. Defaults to\n * `[\"secret\", \"configmap\", \"value\"]` (or drops \"value\" when `allowLiteral` is\n * false). Pass an explicit list to offer helm / serviceaccount / onepassword.\n */\n sources?: SecretValueSource[];\n /**\n * Shorthand honoured only when `sources` is not supplied: `false` drops the\n * inline \"Value\" source, leaving Secret/ConfigMap. Ignored when `sources` is\n * given.\n */\n allowLiteral?: boolean;\n /**\n * When true, a chosen name absent from the loaded resources, or a key absent\n * from the chosen keyed resource's keys, is flagged invalid (once the load\n * settles). The onepassword and literal sources are never strict-flagged.\n */\n strict?: boolean;\n className?: string;\n};\n\nconst DEFAULT_SOURCES: SecretValueSource[] = [\"secret\", \"configmap\", \"value\"];\n\nconst SHRINKABLE_FIELD = \"max-w-full min-w-0 shrink\";\nconst SOURCE_FIELD = `${SHRINKABLE_FIELD} basis-40 grow-0`;\nconst RESOURCE_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow-0 @min-[22rem]/secret:basis-40`;\nconst KEY_FIELD = `${SHRINKABLE_FIELD} w-full basis-auto grow @min-[22rem]/secret:basis-72`;\nconst VALUE_FIELD = `${SHRINKABLE_FIELD} basis-64 grow`;\n\nexport function SecretKeySelector({\n value,\n onChange,\n loadResources,\n loadKeyPreview,\n loadServiceAccounts,\n onePassword,\n sources,\n allowLiteral = true,\n strict = false,\n className,\n}: SecretKeySelectorProps) {\n const sourceList = useMemo<SecretValueSource[]>(\n () => sources ?? (allowLiteral ? DEFAULT_SOURCES : [\"secret\", \"configmap\"]),\n [sources, allowLiteral],\n );\n\n const source: SecretValueSource = value?.kind ?? sourceList[0] ?? \"secret\";\n const isKeyed =\n source === \"secret\" || source === \"configmap\" || source === \"helm\";\n const isServiceAccount = source === \"serviceaccount\";\n const isOnePassword = source === \"onepassword\";\n const isLiteral = source === \"value\";\n // A keyed source's refKind drives the loaders; other sources default to \"secret\".\n const refKind: SecretKind = isKeyed ? (source as SecretKind) : \"secret\";\n\n const selectedName =\n value && (value.kind === \"serviceaccount\" || isRefKind(value))\n ? value.name\n : \"\";\n const selectedKey = value && isRefKind(value) ? value.key : \"\";\n const literalValue = value && value.kind === \"value\" ? value.value : \"\";\n const opRef = value && value.kind === \"onepassword\" ? value.ref : \"\";\n\n const [resources, setResources] = useState<SecretResource[]>([]);\n const [resourcesLoading, setResourcesLoading] = useState(false);\n const [previews, setPreviews] = useState<KeyPreview[]>([]);\n const [previewLoading, setPreviewLoading] = useState(false);\n\n // Load the name list for keyed kinds (secret/configmap/helm) and, when a\n // loader is supplied, for the serviceaccount source. Other sources list nothing.\n useEffect(() => {\n const loader = isKeyed\n ? () => loadResources(refKind)\n : isServiceAccount && loadServiceAccounts\n ? loadServiceAccounts\n : null;\n if (!loader) {\n setResources([]);\n return;\n }\n let cancelled = false;\n setResourcesLoading(true);\n loader()\n .then((res) => !cancelled && setResources(res))\n .finally(() => !cancelled && setResourcesLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadResources, loadServiceAccounts, refKind, isKeyed, isServiceAccount]);\n\n useEffect(() => {\n if (!isKeyed || !selectedName) {\n setPreviews([]);\n return;\n }\n let cancelled = false;\n setPreviewLoading(true);\n loadKeyPreview(refKind, selectedName)\n .then((res) => !cancelled && setPreviews(res))\n .catch(() => !cancelled && setPreviews([]))\n .finally(() => !cancelled && setPreviewLoading(false));\n return () => {\n cancelled = true;\n };\n }, [loadKeyPreview, refKind, selectedName, isKeyed]);\n\n const nameOptions = useMemo<ComboboxOption[]>(\n () => resources.map((r) => ({ value: r.name, label: r.name })),\n [resources],\n );\n const selectedResource = useMemo(\n () => resources.find((r) => r.name === selectedName),\n [resources, selectedName],\n );\n const keyOptions = useMemo(\n () => buildKeyOptions(selectedResource?.keys ?? [], previews, selectedKey),\n [selectedResource, selectedKey, previews],\n );\n\n // Strict validity: the name is invalid when it names no loaded resource; the\n // key is invalid when, with a resolved resource, it isn't one of that\n // resource's keys. Only names from a listed source are checked (a\n // serviceaccount source with no loader lists nothing, so it never flags).\n const listsNames = isKeyed || (isServiceAccount && !!loadServiceAccounts);\n const nameInvalid =\n strict &&\n listsNames &&\n !!selectedName &&\n !resourcesLoading &&\n !selectedResource;\n const keyInvalid =\n strict &&\n isKeyed &&\n !!selectedKey &&\n !!selectedResource &&\n !resourcesLoading &&\n !(selectedResource.keys ?? []).includes(selectedKey);\n\n const setSource = (next: SecretValueSource) => {\n switch (next) {\n case \"value\":\n return onChange({ kind: \"value\", value: \"\" });\n case \"onepassword\":\n return onChange({ kind: \"onepassword\", ref: \"\" });\n case \"serviceaccount\":\n return onChange({ kind: \"serviceaccount\", name: \"\" });\n default:\n return onChange({ kind: next, name: \"\", key: \"\" });\n }\n };\n const setName = (name: string) => {\n if (isServiceAccount)\n return onChange(name ? { kind: \"serviceaccount\", name } : undefined);\n return onChange(\n name ? { kind: refKind, name, key: selectedKey } : undefined,\n );\n };\n const setKey = (key: string) =>\n selectedName\n ? onChange({ kind: refKind, name: selectedName, key })\n : undefined;\n const setLiteral = (v: string) => onChange({ kind: \"value\", value: v });\n const setOpRef = (ref: string) => onChange({ kind: \"onepassword\", ref });\n\n const sourceOptions = useMemo<ComboboxOption[]>(\n () =>\n sourceList.map((k) => ({\n value: k,\n label: SOURCE_LABEL[k],\n icon: createElement(SOURCE_ICON[k], { className: \"size-4\" }),\n })),\n [sourceList],\n );\n const sourceMenuOptions = useMemo<IconMenuOption<SecretValueSource>[]>(\n () =>\n sourceList.map((kind) => ({\n value: kind,\n label: SOURCE_LABEL[kind],\n icon: SOURCE_ICON[kind],\n })),\n [sourceList],\n );\n\n return (\n <div\n data-slot=\"secret-key-selector\"\n className={cn(\"@container/secret w-full min-w-0\", className)}\n >\n <div\n className=\"flex w-full max-w-full min-w-0 flex-nowrap items-center gap-2\"\n data-slot=\"secret-fields\"\n >\n <div\n className={cn(SOURCE_FIELD, \"@max-md:hidden\")}\n data-slot=\"secret-source-combobox\"\n >\n <Combobox\n ariaLabel=\"Secret value source\"\n options={sourceOptions}\n value={source}\n onChange={(next) => setSource(next as SecretValueSource)}\n allowCustomValue={false}\n required\n />\n </div>\n <div\n className=\"hidden shrink-0 @max-md:!block\"\n data-slot=\"secret-source-menu\"\n >\n <IconMenuPicker\n value={source}\n onChange={setSource}\n options={sourceMenuOptions}\n ariaLabel=\"Secret value source\"\n triggerClassName=\"size-control-h rounded-md border border-input bg-background\"\n />\n </div>\n {isLiteral ? (\n <TextField\n value={literalValue}\n onChange={setLiteral}\n placeholder=\"Static value…\"\n ariaLabel=\"Static value\"\n />\n ) : isOnePassword ? (\n (() => {\n if (!onePassword) {\n throw new Error(\n \"SecretKeySelector onepassword source requires onePassword loaders\",\n );\n }\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-onepassword-field\">\n <OnePasswordSelector\n value={opRef}\n onChange={setOpRef}\n {...onePassword}\n allowCustomValue\n />\n </div>\n );\n })()\n ) : isServiceAccount ? (\n <div\n className={VALUE_FIELD}\n data-slot=\"secret-serviceaccount-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder=\"Service account…\"\n />\n </div>\n ) : (\n <div\n className={cn(\n \"flex flex-col items-center gap-2 @min-[22rem]/secret:flex-row\",\n SHRINKABLE_FIELD,\n \"grow\",\n )}\n data-slot=\"secret-reference-fields\"\n >\n <div\n className={RESOURCE_FIELD}\n data-slot=\"secret-resource-field\"\n >\n <Combobox\n options={nameOptions}\n value={selectedName}\n onChange={setName}\n allowCustomValue\n loading={resourcesLoading}\n invalid={nameInvalid}\n placeholder={`Select ${refKind}…`}\n />\n </div>\n <div className={KEY_FIELD} data-slot=\"secret-key-field\">\n <Combobox\n options={keyOptions}\n value={selectedKey}\n onChange={setKey}\n allowCustomValue\n loading={previewLoading}\n invalid={keyInvalid}\n placeholder={selectedName ? keyPlaceholder(refKind) : \"—\"}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction TextField({\n value,\n onChange,\n placeholder,\n ariaLabel,\n}: {\n value: string;\n onChange: (v: string) => void;\n placeholder: string;\n ariaLabel: string;\n}) {\n return (\n <div className={VALUE_FIELD} data-slot=\"secret-value-fields\">\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n className={cn(\n \"h-control-h w-full min-w-0 rounded border border-input bg-background px-control-px text-sm\",\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n />\n </div>\n );\n}\n\n// isRefKind narrows to the keyed variants that carry both a name and a key.\nfunction isRefKind(\n v: SecretKeyValue,\n): v is { kind: SecretKind; name: string; key: string } {\n return v.kind === \"secret\" || v.kind === \"configmap\" || v.kind === \"helm\";\n}\n\n// keyPlaceholder hints the key field: helm keys are jsonpath expressions into\n// the merged release values, the others are literal data keys.\nfunction keyPlaceholder(kind: SecretKind): string {\n return kind === \"helm\" ? \"jsonpath…\" : \"Key…\";\n}\n\n// Each source's leading glyph. The flanksource FCs carry static metadata that\n// doesn't unify with ComponentType, so type the slot as a plain render function.\nconst SOURCE_ICON: Record<SecretValueSource, StaticIconComponent> = {\n secret: K8SSecret,\n configmap: K8SConfigmap,\n helm: Helm,\n serviceaccount: K8SServiceaccount,\n onepassword: Vault,\n value: UiEdit,\n};\n\nconst SOURCE_LABEL: Record<SecretValueSource, string> = {\n secret: \"Secret\",\n configmap: \"ConfigMap\",\n helm: \"Helm\",\n serviceaccount: \"Service Account\",\n onepassword: \"1Password\",\n value: \"Value\",\n};\n\n// buildKeyOptions labels each key with its mid-masked preview value when one is\n// available. A selected key absent from the list is pinned so the selection\n// stays visible.\nfunction buildKeyOptions(\n keys: string[],\n previews: KeyPreview[],\n selectedKey: string,\n): ComboboxOption[] {\n const masked = new Map(previews.map((p) => [p.key, p.value]));\n const base = keys.map((k) => ({\n value: k,\n label: masked.has(k) ? `${k} — ${masked.get(k)}` : k,\n }));\n if (selectedKey && !base.some((o) => o.value === selectedKey)) {\n return [{ value: selectedKey, label: selectedKey }, ...base];\n }\n return base;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ const countsFromSummary = (summary) => ({
|
|
|
36
36
|
skipped: summary.Skipped,
|
|
37
37
|
pending: summary.Pending || 0,
|
|
38
38
|
running: summary.Running || 0,
|
|
39
|
-
timedout: 0
|
|
39
|
+
timedout: summary.Timedout || 0
|
|
40
40
|
});
|
|
41
41
|
const countsFromLeaf = (t) => {
|
|
42
42
|
const isTimedOut = !!t.timed_out;
|