mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-10-19 01:50:51 -05:00
refactor(opal): collect the input families into organizational directories (#14681)
This commit is contained in:
1 parent
02234448f4
commit
a0370f232b
82 files changed
+269
-277
No files matched your search
+2
-2
@@ -155,9 +155,9 @@
|
||||
"date-fns": "^4.0.0",
|
||||
"formik": "^2.0.0",
|
||||
"next": ">=14.0.0",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^9.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-dropzone": "^14.0.0 || ^20.0.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
|
||||
@@ -107,9 +107,9 @@
|
||||
"date-fns": "^4.0.0",
|
||||
"formik": "^2.0.0",
|
||||
"next": ">=14.0.0",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^9.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-dropzone": "^14.0.0 || ^20.0.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
|
||||
@@ -21,14 +21,16 @@ The barrel file at `index.ts` re-exports each component and its prop types. Each
|
||||
| [SelectButton](./buttons/select-button/) | Stateful toggle button with optional foldable content | [README](./buttons/select-button/README.md) |
|
||||
| [OpenButton](./buttons/open-button/) | Trigger button with rotating chevron for popovers | [README](./buttons/open-button/README.md) |
|
||||
| [Tag](./tag/) | Small colored label for status/category metadata | [README](./tag/README.md) |
|
||||
| [DateRangePicker](./inputs/date-range-picker/) | Preset date-range strip with a custom-range calendar popover | [README](./inputs/date-range-picker/README.md) |
|
||||
| [InputDateRangePicker](./inputs/chrono/input-date-range-picker/) | Preset date-range strip with a custom-range calendar popover | [README](./inputs/chrono/input-date-range-picker/README.md) |
|
||||
| [InputFile](./inputs/input-file/) | Text field that doubles as a file picker | [README](./inputs/input-file/README.md) |
|
||||
| [InputAvatar](./inputs/input-avatar/) | Avatar frame with primary input chrome | [README](./inputs/input-avatar/README.md) |
|
||||
| [InputComboBox](./inputs/input-combo-box/) | Filterable input/select hybrid with create-new support | [README](./inputs/input-combo-box/README.md) |
|
||||
| [InputComboBox](./inputs/selections/input-combo-box/) | Filterable input/select hybrid with create-new support | [README](./inputs/selections/input-combo-box/README.md) |
|
||||
| [InputImage](./inputs/input-image/) | Circular image dropzone with edit overlay | [README](./inputs/input-image/README.md) |
|
||||
| [InputKeyValue](./inputs/input-key-value/) | Key/value pair editor with validation | [README](./inputs/input-key-value/README.md) |
|
||||
| [InputSingleSelect](./inputs/input-select/input-single-select/) | Styled dropdown on Radix Select, pick exactly one | [README](./inputs/input-select/input-single-select/README.md) |
|
||||
| [InputMultiSelect](./inputs/input-select/input-multi-select/) | Chips-in-input multi selection (Figma Input/Tags) | [README](./inputs/input-select/input-multi-select/README.md) |
|
||||
| [InputSingleSelect](./inputs/selections/input-single-select/) | Styled dropdown on Radix Select, pick exactly one | [README](./inputs/selections/input-single-select/README.md) |
|
||||
| [InputMultiSelect](./inputs/selections/input-multi-select/) | Chips-in-input multi selection (Figma Input/Tags) | [README](./inputs/selections/input-multi-select/README.md) |
|
||||
| [InputCheckbox](./inputs/booleans/input-checkbox/) | Checkbox with checked/indeterminate states | [README](./inputs/booleans/input-checkbox/README.md) |
|
||||
| [InputSwitch](./inputs/booleans/input-switch/) | On/off toggle switch | [README](./inputs/booleans/input-switch/README.md) |
|
||||
| [InputNumber](./inputs/input-number/) | Number field with steppers and reset | [README](./inputs/input-number/README.md) |
|
||||
| [InputList](./inputs/input-list/) | Type-and-Enter list builder with removable chips below | [README](./inputs/input-list/README.md) |
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ rounding (`--radius-12`).
|
||||
```
|
||||
root Interactive.Stateful + Interactive.Container
|
||||
├─ title tile + icon-less Content (main-ui / section, frozen)
|
||||
│ ├─ tile icon on tint, or image cover-filling; Checkbox when selected
|
||||
│ ├─ tile icon on tint, or image cover-filling; InputCheckbox when selected
|
||||
│ └─ Content title, description, optional inline editing
|
||||
├─ center centerChildren — flex-1, caller-aligned
|
||||
└─ action rightChildren — min-width --spacing-block-36, always reserved
|
||||
@@ -26,7 +26,7 @@ Exactly one of two arms, enforced at the type level:
|
||||
- `imageSrc` + `imageAlt` — image fills the tile (`object-fit: cover`).
|
||||
`imageAlt` is mandatory: the image is content, not chrome.
|
||||
|
||||
When `state="selected"`, the tile swaps its content for a checked `Checkbox`.
|
||||
When `state="selected"`, the tile swaps its content for a checked `InputCheckbox`.
|
||||
|
||||
## Interactivity
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
IconFunctionComponent,
|
||||
RichStr,
|
||||
} from "@opal/types";
|
||||
import { Checkbox, Tooltip, type TooltipSide } from "@opal/components";
|
||||
import { InputCheckbox, Tooltip, type TooltipSide } from "@opal/components";
|
||||
import {
|
||||
composeKeyHandler,
|
||||
guardNestedInteractiveClick,
|
||||
@@ -253,7 +253,7 @@ function AttachmentItemButton({
|
||||
the event flow — the row alone owns activation, and its
|
||||
own selected styling carries the state for AT. */
|
||||
<span inert aria-hidden className="contents">
|
||||
<Checkbox checked readOnly />
|
||||
<InputCheckbox checked readOnly />
|
||||
</span>
|
||||
) : imageSrc ? (
|
||||
<img
|
||||
|
||||
@@ -60,14 +60,14 @@ export {
|
||||
type TextButtonProps,
|
||||
} from "@opal/components/buttons/text-button/components";
|
||||
|
||||
/* DateRangePicker */
|
||||
/* InputDateRangePicker */
|
||||
export {
|
||||
DateRangePicker,
|
||||
InputDateRangePicker,
|
||||
rangeForInclusiveDays,
|
||||
THIRTY_DAYS,
|
||||
type DateRange,
|
||||
type DateRangePickerValue,
|
||||
} from "@opal/components/inputs/date-range-picker/components";
|
||||
type InputDateRangePickerValue,
|
||||
} from "@opal/components/inputs/chrono/input-date-range-picker/components";
|
||||
|
||||
/* InputAvatar */
|
||||
export { default as InputAvatar } from "@opal/components/inputs/input-avatar/components";
|
||||
@@ -84,7 +84,7 @@ export {
|
||||
default as InputComboBox,
|
||||
type InputComboBoxProps,
|
||||
type ComboBoxOption,
|
||||
} from "@opal/components/inputs/input-combo-box/index";
|
||||
} from "@opal/components/inputs/selections/input-combo-box/index";
|
||||
|
||||
/* InputImage */
|
||||
export {
|
||||
@@ -195,11 +195,11 @@ export {
|
||||
type CalendarProps,
|
||||
} from "@opal/components/calendar/components";
|
||||
|
||||
/* Checkbox */
|
||||
/* InputCheckbox */
|
||||
export {
|
||||
Checkbox,
|
||||
type CheckboxProps,
|
||||
} from "@opal/components/checkbox/components";
|
||||
InputCheckbox,
|
||||
type InputCheckboxProps,
|
||||
} from "@opal/components/inputs/booleans/input-checkbox/components";
|
||||
|
||||
/* Table */
|
||||
export { Table } from "@opal/components/table/components";
|
||||
@@ -249,7 +249,7 @@ export {
|
||||
export {
|
||||
InputDatePicker,
|
||||
type InputDatePickerProps,
|
||||
} from "@opal/components/inputs/input-date-picker/components";
|
||||
} from "@opal/components/inputs/chrono/input-date-picker/components";
|
||||
|
||||
/* InputSingleSelect */
|
||||
export {
|
||||
@@ -258,14 +258,14 @@ export {
|
||||
type InputSingleSelectTriggerProps,
|
||||
type InputSingleSelectItemProps,
|
||||
type InputSingleSelectSearchProps,
|
||||
} from "@opal/components/inputs/input-select/input-single-select/components";
|
||||
} from "@opal/components/inputs/selections/input-single-select/components";
|
||||
|
||||
/* InputMultiSelect */
|
||||
export {
|
||||
InputMultiSelect,
|
||||
type InputMultiSelectProps,
|
||||
type TagItem,
|
||||
} from "@opal/components/inputs/input-select/input-multi-select/components";
|
||||
} from "@opal/components/inputs/selections/input-multi-select/components";
|
||||
|
||||
/* InputPasswordTypeIn */
|
||||
export {
|
||||
@@ -284,7 +284,7 @@ export {
|
||||
InputTime,
|
||||
type InputTimeProps,
|
||||
type TimeValue,
|
||||
} from "@opal/components/inputs/input-time/components";
|
||||
} from "@opal/components/inputs/chrono/input-time/components";
|
||||
|
||||
/* Spacer */
|
||||
export { Spacer, type SpacerProps } from "@opal/components/spacer/components";
|
||||
@@ -293,7 +293,7 @@ export { Spacer, type SpacerProps } from "@opal/components/spacer/components";
|
||||
export {
|
||||
InputSwitch,
|
||||
type InputSwitchProps,
|
||||
} from "@opal/components/inputs/input-switch/components";
|
||||
} from "@opal/components/inputs/booleans/input-switch/components";
|
||||
|
||||
/* CopyButton */
|
||||
export {
|
||||
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Checkbox, Text } from "@opal/components";
|
||||
import { InputCheckbox, Text } from "@opal/components";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Checkbox",
|
||||
component: Checkbox,
|
||||
title: "opal/Inputs/InputCheckbox",
|
||||
component: InputCheckbox,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
} satisfies Meta<typeof Checkbox>;
|
||||
} satisfies Meta<typeof InputCheckbox>;
|
||||
|
||||
export default meta;
|
||||
|
||||
@@ -54,7 +54,7 @@ export const DisabledChecked: Story = {
|
||||
export const WithLabel: StoryObj = {
|
||||
render: () => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="terms" />
|
||||
<InputCheckbox id="terms" />
|
||||
<label htmlFor="terms" className="cursor-pointer">
|
||||
<Text font="main-ui-body" color="text-04">
|
||||
Accept terms and conditions
|
||||
@@ -82,7 +82,7 @@ export const AllStates: StoryObj = {
|
||||
] as const
|
||||
).map(([label, props]) => (
|
||||
<div key={label} className="flex items-center gap-3">
|
||||
<Checkbox {...props} />
|
||||
<InputCheckbox {...props} />
|
||||
<Text font="secondary-body" color="text-03">
|
||||
{label}
|
||||
</Text>
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
# Checkbox
|
||||
# InputCheckbox
|
||||
|
||||
**Import:** `import { Checkbox, type CheckboxProps } from "@opal/components";`
|
||||
**Import:** `import { InputCheckbox, type InputCheckboxProps } from "@opal/components";`
|
||||
|
||||
A dual-element checkbox with custom styling. Uses a hidden native `<input>` for form state and a visible `<div>` for the visual surface. Supports controlled, uncontrolled, indeterminate, and disabled modes.
|
||||
|
||||
@@ -50,23 +50,23 @@ defined in `styles.css`:
|
||||
## Usage Examples
|
||||
|
||||
```tsx
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
|
||||
// Uncontrolled
|
||||
<Checkbox onCheckedChange={(checked) => console.log(checked)} />
|
||||
<InputCheckbox onCheckedChange={(checked) => console.log(checked)} />
|
||||
|
||||
// Controlled
|
||||
<Checkbox checked={isChecked} onCheckedChange={setIsChecked} />
|
||||
<InputCheckbox checked={isChecked} onCheckedChange={setIsChecked} />
|
||||
|
||||
// With label
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="terms" />
|
||||
<InputCheckbox id="terms" />
|
||||
<label htmlFor="terms">Accept terms</label>
|
||||
</div>
|
||||
|
||||
// Indeterminate (e.g. "select all" with partial selection)
|
||||
<Checkbox indeterminate />
|
||||
<InputCheckbox indeterminate />
|
||||
|
||||
// Disabled
|
||||
<Checkbox disabled checked />
|
||||
<InputCheckbox disabled checked />
|
||||
```
|
||||
+28
-33
@@ -1,8 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import "@opal/components/checkbox/styles.css";
|
||||
import "@opal/components/inputs/booleans/input-checkbox/styles.css";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { cn } from "@opal/utils";
|
||||
import { SvgCheck, SvgMinus } from "@opal/icons";
|
||||
import type { WithoutStyles } from "@opal/types";
|
||||
|
||||
@@ -12,17 +11,8 @@ import type { WithoutStyles } from "@opal/types";
|
||||
|
||||
type CheckboxState = "unchecked" | "checked" | "indeterminate";
|
||||
|
||||
interface CheckboxProps extends WithoutStyles<
|
||||
Omit<React.ComponentPropsWithoutRef<"input">, "type" | "size">
|
||||
> {
|
||||
checked?: boolean;
|
||||
defaultChecked?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
indeterminate?: boolean;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Checkbox
|
||||
// InputCheckbox
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -35,23 +25,31 @@ interface CheckboxProps extends WithoutStyles<
|
||||
*
|
||||
* All visual states are driven by CSS in `styles.css`.
|
||||
*/
|
||||
function CheckboxInner(
|
||||
{
|
||||
checked: controlledChecked,
|
||||
defaultChecked,
|
||||
onCheckedChange,
|
||||
indeterminate = false,
|
||||
disabled,
|
||||
onChange,
|
||||
id,
|
||||
name,
|
||||
"aria-label": ariaLabel,
|
||||
"aria-labelledby": ariaLabelledby,
|
||||
"aria-describedby": ariaDescribedby,
|
||||
...props
|
||||
}: CheckboxProps,
|
||||
ref: React.ForwardedRef<HTMLInputElement>
|
||||
) {
|
||||
type InputCheckboxProps = WithoutStyles<
|
||||
Omit<React.ComponentPropsWithoutRef<"input">, "type" | "size">
|
||||
> & {
|
||||
checked?: boolean;
|
||||
defaultChecked?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
indeterminate?: boolean;
|
||||
/** Ref to the hidden `<input>`. */
|
||||
ref?: React.Ref<HTMLInputElement>;
|
||||
};
|
||||
function InputCheckbox({
|
||||
checked: controlledChecked,
|
||||
defaultChecked,
|
||||
onCheckedChange,
|
||||
indeterminate = false,
|
||||
disabled,
|
||||
onChange,
|
||||
id,
|
||||
name,
|
||||
"aria-label": ariaLabel,
|
||||
"aria-labelledby": ariaLabelledby,
|
||||
"aria-describedby": ariaDescribedby,
|
||||
ref,
|
||||
...props
|
||||
}: InputCheckboxProps) {
|
||||
const [uncontrolledChecked, setUncontrolledChecked] = useState(
|
||||
defaultChecked ?? false
|
||||
);
|
||||
@@ -161,7 +159,4 @@ function CheckboxInner(
|
||||
);
|
||||
}
|
||||
|
||||
const Checkbox = React.forwardRef(CheckboxInner);
|
||||
Checkbox.displayName = "Checkbox";
|
||||
export default Checkbox;
|
||||
export { Checkbox, type CheckboxProps };
|
||||
export { InputCheckbox, type InputCheckboxProps, type CheckboxState };
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
@reference "#reference.css";
|
||||
|
||||
/* ============================================================================
|
||||
Checkbox — state x disabled color matrix
|
||||
InputCheckbox — state x disabled color matrix
|
||||
|
||||
Visual styling is driven by two data attributes on the root element:
|
||||
- `data-state`: "unchecked" | "checked" | "indeterminate"
|
||||
File renamed without changes.
+1
-1
@@ -28,7 +28,7 @@ import { InputSwitch } from "@opal/components";
|
||||
| `onCheckedChange` | `(checked: boolean) => void` | `undefined` | Called with the new value when toggled. |
|
||||
| `disabled` | `boolean` | `false` | Disables interaction and applies muted styles. |
|
||||
|
||||
All other `<button>` attributes (except `style`, `className`, and `onChange`) are forwarded.
|
||||
All other `<button>` attributes (except `style`, `className`, `onChange`, and `type` — the switch is always `type="button"`) are forwarded.
|
||||
|
||||
## Visual States
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
"use client";
|
||||
|
||||
import "@opal/components/inputs/booleans/input-switch/styles.css";
|
||||
import React, { useState } from "react";
|
||||
import { cn } from "@opal/utils";
|
||||
import type { WithoutStyles } from "@opal/types";
|
||||
|
||||
/**
|
||||
* Accessible toggle switch. Supports controlled and uncontrolled modes.
|
||||
*
|
||||
* - Controlled: pass `checked` + `onCheckedChange`.
|
||||
* - Uncontrolled: pass `defaultChecked` (defaults to `false`).
|
||||
*/
|
||||
type InputSwitchProps = WithoutStyles<
|
||||
Omit<React.ComponentPropsWithoutRef<"button">, "onChange" | "type">
|
||||
> & {
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
defaultChecked?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
/** Ref to the switch `<button>`. */
|
||||
ref?: React.Ref<HTMLButtonElement>;
|
||||
};
|
||||
function InputSwitch({
|
||||
ref,
|
||||
disabled,
|
||||
checked: controlledChecked,
|
||||
defaultChecked,
|
||||
onCheckedChange,
|
||||
onClick,
|
||||
...props
|
||||
}: InputSwitchProps) {
|
||||
const [uncontrolledChecked, setUncontrolledChecked] = useState(
|
||||
defaultChecked ?? false
|
||||
);
|
||||
|
||||
const isControlled = controlledChecked !== undefined;
|
||||
const checked = isControlled ? controlledChecked : uncontrolledChecked;
|
||||
|
||||
function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
|
||||
if (disabled) return;
|
||||
|
||||
const newChecked = !checked;
|
||||
|
||||
if (!isControlled) setUncontrolledChecked(newChecked);
|
||||
onClick?.(event);
|
||||
onCheckedChange?.(newChecked);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
// Spread first: type/role/aria-checked/disabled/onClick are this
|
||||
// control's contract and must not be overridable through the spread.
|
||||
{...props}
|
||||
ref={ref}
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
className={cn(
|
||||
"peer inline-flex h-4.5 w-8 shrink-0 cursor-pointer items-center rounded-full transition-colors focus-visible:outline-hidden",
|
||||
disabled
|
||||
? checked
|
||||
? "switch-disabled-checked"
|
||||
: "switch-disabled"
|
||||
: checked
|
||||
? "switch-normal-checked"
|
||||
: "switch-normal"
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"pointer-events-none block h-3.5 w-3.5 rounded-full ring-0 transition-transform",
|
||||
// rtl: the knob travels toward the inline end, so RTL negates.
|
||||
checked
|
||||
? "translate-x-[15px] rtl:-translate-x-[15px]"
|
||||
: "translate-x-px rtl:-translate-x-px",
|
||||
disabled ? "switch-thumb-disabled" : "switch-thumb"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export { InputSwitch, type InputSwitchProps };
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
-7
@@ -1,13 +1,13 @@
|
||||
import { useState } from "react";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import {
|
||||
DateRangePicker,
|
||||
InputDateRangePicker,
|
||||
type DateRange,
|
||||
} from "@opal/components/inputs/date-range-picker/components";
|
||||
} from "@opal/components/inputs/chrono/input-date-range-picker/components";
|
||||
|
||||
const meta: Meta<typeof DateRangePicker> = {
|
||||
title: "components/DateRangePicker",
|
||||
component: DateRangePicker,
|
||||
const meta: Meta<typeof InputDateRangePicker> = {
|
||||
title: "components/InputDateRangePicker",
|
||||
component: InputDateRangePicker,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
@@ -22,12 +22,12 @@ const meta: Meta<typeof DateRangePicker> = {
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DateRangePicker>;
|
||||
type Story = StoryObj<typeof InputDateRangePicker>;
|
||||
|
||||
function ControlledRange({ initialValue }: { initialValue: DateRange }) {
|
||||
const [value, setValue] = useState<DateRange>(initialValue);
|
||||
|
||||
return <DateRangePicker value={value} onValueChange={setValue} />;
|
||||
return <InputDateRangePicker value={value} onValueChange={setValue} />;
|
||||
}
|
||||
|
||||
export const Default: Story = {
|
||||
+8
-8
@@ -1,8 +1,8 @@
|
||||
import { render, screen } from "@tests/setup/test-utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { DateRangePicker } from "@opal/components";
|
||||
import { InputDateRangePicker } from "@opal/components";
|
||||
|
||||
describe("DateRangePicker", () => {
|
||||
describe("InputDateRangePicker", () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(new Date(2026, 7, 4, 12));
|
||||
@@ -17,7 +17,7 @@ describe("DateRangePicker", () => {
|
||||
const onValueChange = jest.fn();
|
||||
|
||||
render(
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={{
|
||||
from: new Date(2026, 7, 1),
|
||||
to: new Date(2026, 7, 4),
|
||||
@@ -55,7 +55,7 @@ describe("DateRangePicker", () => {
|
||||
const onValueChange = jest.fn();
|
||||
|
||||
render(
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={{
|
||||
from: new Date(2026, 6, 5),
|
||||
to: new Date(2026, 7, 4),
|
||||
@@ -77,7 +77,7 @@ describe("DateRangePicker", () => {
|
||||
const onValueChange = jest.fn();
|
||||
|
||||
render(
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={{
|
||||
from: new Date(2026, 6, 6),
|
||||
to: new Date(2026, 7, 4),
|
||||
@@ -99,7 +99,7 @@ describe("DateRangePicker", () => {
|
||||
const onValueChange = jest.fn();
|
||||
|
||||
render(
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={{
|
||||
from: new Date(2026, 6, 6),
|
||||
to: new Date(2026, 7, 4),
|
||||
@@ -121,7 +121,7 @@ describe("DateRangePicker", () => {
|
||||
const onValueChange = jest.fn();
|
||||
|
||||
render(
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={{
|
||||
from: new Date(2026, 6, 6),
|
||||
to: new Date(2026, 7, 4),
|
||||
@@ -142,7 +142,7 @@ describe("DateRangePicker", () => {
|
||||
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
|
||||
|
||||
render(
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={{
|
||||
from: new Date(2026, 6, 10),
|
||||
to: new Date(2026, 6, 15),
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# DateRangePicker
|
||||
# InputDateRangePicker
|
||||
|
||||
**Import:** `import { DateRangePicker, rangeForInclusiveDays, type DateRange } from "@opal/components";`
|
||||
**Import:** `import { InputDateRangePicker, rangeForInclusiveDays, type DateRange } from "@opal/components";`
|
||||
|
||||
A preset-strip date-range control: 1D / 7D / 1M / 3M buttons plus a "Custom" trigger that opens
|
||||
the Opal `Calendar` in a popover for an arbitrary range. The active custom range renders as its
|
||||
+2
-2
@@ -8,7 +8,7 @@ import { useOpalStrings, type OpalStrings } from "@opal/strings";
|
||||
|
||||
export const THIRTY_DAYS = "1M";
|
||||
|
||||
export type DateRangePickerValue = DateRange & {
|
||||
export type InputDateRangePickerValue = DateRange & {
|
||||
selectValue: string;
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ function presetDisplayLabel(strings: OpalStrings, preset: DatePreset): string {
|
||||
}
|
||||
}
|
||||
|
||||
export const DateRangePicker = memo(function DateRangePicker({
|
||||
export const InputDateRangePicker = memo(function InputDateRangePicker({
|
||||
value,
|
||||
onValueChange,
|
||||
size = "md",
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,97 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import "@opal/components/inputs/input-switch/styles.css";
|
||||
import React, { useState } from "react";
|
||||
import { cn } from "@opal/utils";
|
||||
import type { WithoutStyles } from "@opal/types";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface InputSwitchProps extends WithoutStyles<
|
||||
Omit<React.ComponentPropsWithoutRef<"button">, "onChange">
|
||||
> {
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
defaultChecked?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Component
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Accessible toggle switch. Supports controlled and uncontrolled modes.
|
||||
*
|
||||
* - Controlled: pass `checked` + `onCheckedChange`.
|
||||
* - Uncontrolled: pass `defaultChecked` (defaults to `false`).
|
||||
*/
|
||||
const InputSwitch = React.forwardRef<HTMLButtonElement, InputSwitchProps>(
|
||||
(
|
||||
{
|
||||
disabled,
|
||||
checked: controlledChecked,
|
||||
defaultChecked,
|
||||
onCheckedChange,
|
||||
onClick,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [uncontrolledChecked, setUncontrolledChecked] = useState(
|
||||
defaultChecked ?? false
|
||||
);
|
||||
|
||||
const isControlled = controlledChecked !== undefined;
|
||||
const checked = isControlled ? controlledChecked : uncontrolledChecked;
|
||||
|
||||
function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
|
||||
if (disabled) return;
|
||||
|
||||
const newChecked = !checked;
|
||||
|
||||
if (!isControlled) setUncontrolledChecked(newChecked);
|
||||
onClick?.(event);
|
||||
onCheckedChange?.(newChecked);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
className={cn(
|
||||
"peer inline-flex h-4.5 w-8 shrink-0 cursor-pointer items-center rounded-full transition-colors focus-visible:outline-hidden",
|
||||
disabled
|
||||
? checked
|
||||
? "switch-disabled-checked"
|
||||
: "switch-disabled"
|
||||
: checked
|
||||
? "switch-normal-checked"
|
||||
: "switch-normal"
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
{...props}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"pointer-events-none block h-3.5 w-3.5 rounded-full ring-0 transition-transform",
|
||||
// rtl: the knob travels toward the inline end, so RTL negates.
|
||||
checked
|
||||
? "translate-x-[15px] rtl:-translate-x-[15px]"
|
||||
: "translate-x-px rtl:-translate-x-px",
|
||||
disabled ? "switch-thumb-disabled" : "switch-thumb"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
InputSwitch.displayName = "InputSwitch";
|
||||
|
||||
export default InputSwitch;
|
||||
export { InputSwitch };
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -3,7 +3,7 @@
|
||||
import "@opal/components/inputs/shared.css";
|
||||
// The inner field reuses InputTypeIn's .opal-input-field styling.
|
||||
import "@opal/components/inputs/input-type-in/styles.css";
|
||||
import "@opal/components/inputs/input-select/input-multi-select/styles.css";
|
||||
import "@opal/components/inputs/selections/input-multi-select/styles.css";
|
||||
import { useEffect, useRef } from "react";
|
||||
import type { IconFunctionComponent } from "@opal/types";
|
||||
import { Button, Tag, TAG_REMOVE_CLASS } from "@opal/components";
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import "@opal/components/inputs/shared.css";
|
||||
import "@opal/components/inputs/input-select/input-single-select/styles.css";
|
||||
import "@opal/components/inputs/selections/input-single-select/styles.css";
|
||||
import React from "react";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { cn } from "@opal/utils";
|
||||
File renamed without changes.
@@ -61,4 +61,4 @@ When a `[data-main-container]` element is present (the content area beside the s
|
||||
|
||||
## Deferred
|
||||
|
||||
The remaining Figma header/footer content variants (Search, Card, Panel, Checkbox, Message) are follow-ups.
|
||||
The remaining Figma header/footer content variants (Search, Card, Panel, InputCheckbox, Message) are follow-ups.
|
||||
@@ -5,7 +5,7 @@ import { cn } from "@opal/utils";
|
||||
import { useTableSize } from "@opal/components/table/TableSizeContext";
|
||||
import type { IconFunctionComponent } from "@opal/types";
|
||||
import type { QualifierContentType } from "@opal/components/table/types";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
|
||||
interface TableQualifierProps {
|
||||
/** Content type displayed in the qualifier */
|
||||
@@ -118,7 +118,7 @@ function TableQualifier({
|
||||
content === "simple" ? "flex" : overlayStyles
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={selected}
|
||||
onCheckedChange={onSelectChange}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -21,7 +21,7 @@ import QualifierContainer from "@opal/components/table/QualifierContainer";
|
||||
import ActionsContainer from "@opal/components/table/ActionsContainer";
|
||||
import DragOverlayRow from "@opal/components/table/DragOverlayRow";
|
||||
import Footer from "@opal/components/table/Footer";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import { TableSizeProvider } from "@opal/components/table/TableSizeContext";
|
||||
import { ColumnVisibilityPopover } from "@opal/components/table/ColumnVisibilityPopover";
|
||||
import { SortingPopover } from "@opal/components/table/ColumnSortabilityPopover";
|
||||
@@ -387,7 +387,7 @@ export function Table<TData>(props: DataTableProps<TData>) {
|
||||
return (
|
||||
<QualifierContainer key={header.id} type="head">
|
||||
{isMultiSelect && (
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={isAllRowsSelected}
|
||||
indeterminate={
|
||||
!isAllRowsSelected && selectedCount > 0
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Button } from "@opal/components";
|
||||
import { cn } from "@opal/utils";
|
||||
import { SvgChevronDownSmall, SvgTrash } from "@opal/icons";
|
||||
|
||||
function Checkbox({
|
||||
function InputCheckbox({
|
||||
label,
|
||||
checked,
|
||||
onChange,
|
||||
@@ -142,7 +142,7 @@ export const ExistingSlackBotForm = ({
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<div className="inline-block border rounded-lg border-background-200 p-2">
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
label={t("updateForm.enabledCheckbox.label")}
|
||||
checked={formValues.enabled}
|
||||
onChange={(e) => handleUpdateField("enabled", e.target.checked)}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import {
|
||||
updateConnectorFiles,
|
||||
type ConnectorFileInfo,
|
||||
@@ -246,7 +246,7 @@ export default function InlineFileManagement({
|
||||
>
|
||||
{isEditing && (
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={isMarkedForRemoval}
|
||||
onCheckedChange={() =>
|
||||
toggleFileForRemoval(file.file_id)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { OnyxDocument } from "@/lib/search/interfaces";
|
||||
import { buildDocumentSummaryDisplay } from "@/components/search/DocumentDisplay";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import { updateHiddenStatus } from "../lib";
|
||||
import { toast } from "@opal/layouts";
|
||||
import { getErrorMsg } from "@/lib/fetchUtils";
|
||||
@@ -98,7 +98,7 @@ const DocumentDisplay = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="ms-1 my-auto">
|
||||
<Checkbox checked={!document.hidden} />
|
||||
<InputCheckbox checked={!document.hidden} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { numToDisplay } from "./constants";
|
||||
import { FiEye, FiEyeOff } from "react-icons/fi";
|
||||
import { getErrorMsg } from "@/lib/fetchUtils";
|
||||
import { HoverPopup } from "@/components/HoverPopup";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import { ScoreSection } from "../ScoreEditor";
|
||||
import { truncateString } from "@/lib/utils";
|
||||
import { clickOnKeyDown } from "@opal/utils";
|
||||
@@ -48,7 +48,7 @@ const IsVisibleSection = ({
|
||||
>
|
||||
<div className="select-none">{t("visibility.hidden.label")}</div>
|
||||
<div className="ms-1 my-auto">
|
||||
<Checkbox checked={false} />
|
||||
<InputCheckbox checked={false} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -64,7 +64,7 @@ const IsVisibleSection = ({
|
||||
{t("visibility.visible.label")}
|
||||
</div>
|
||||
<div className="ms-1 my-auto">
|
||||
<Checkbox checked={true} />
|
||||
<InputCheckbox checked={true} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useSettings } from "@/lib/settings/hooks";
|
||||
import { Button } from "@opal/components";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { FormField } from "@/refresh-components/form/FormField";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import { useEffect, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
@@ -130,7 +130,7 @@ export function AppPopup() {
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<FormField.Control>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
aria-label={t("consentCheckbox.label")}
|
||||
checked={consentChecked}
|
||||
onCheckedChange={(checked) => {
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
type ModelPrice,
|
||||
} from "@/app/app/settings/usage/lib";
|
||||
import {
|
||||
DateRangePicker,
|
||||
InputDateRangePicker,
|
||||
rangeForInclusiveDays,
|
||||
type DateRange,
|
||||
} from "@opal/components";
|
||||
@@ -448,7 +448,7 @@ export default function UsageSettings() {
|
||||
<Section gap={0.75} justifyContent="start">
|
||||
<div className="flex w-full flex-wrap items-center justify-between gap-3">
|
||||
<Text font="heading-h3">{t("header.title")}</Text>
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={dateRange}
|
||||
onValueChange={setDateRange}
|
||||
size="sm"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Card, Checkbox, Text } from "@opal/components";
|
||||
import { Card, InputCheckbox, Text } from "@opal/components";
|
||||
import { SvgMcp } from "@opal/icons";
|
||||
import type { IconFunctionComponent } from "@opal/types";
|
||||
import { cn } from "@opal/utils";
|
||||
@@ -213,7 +213,7 @@ function PreApprovalRow({ option, checked, onToggle }: PreApprovalRowProps) {
|
||||
{option.status}
|
||||
</Text>
|
||||
</div>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
id={checkboxId}
|
||||
aria-label={option.name}
|
||||
aria-describedby={statusId}
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Checkbox Cell
|
||||
/* InputCheckbox Cell
|
||||
Figma specs:
|
||||
- Width: 24px (1.5rem)
|
||||
- Centered content
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import {
|
||||
DateRange,
|
||||
DateRangePicker,
|
||||
InputDateRangePicker,
|
||||
rangeForInclusiveDays,
|
||||
} from "@opal/components";
|
||||
import { useCallback, useState } from "react";
|
||||
@@ -54,7 +54,7 @@ export default function QueryHistoryPage() {
|
||||
title={adminRouteTitle(route)}
|
||||
divider
|
||||
rightChildren={
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={dateRange}
|
||||
onValueChange={onTimeRangeChange}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useAdminRouteTitle } from "@/lib/adminNavLabels";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { DateRangePicker } from "@opal/components";
|
||||
import { InputDateRangePicker } from "@opal/components";
|
||||
import { useTimeRange } from "@/lib/usage/hooks";
|
||||
import PerUserUsagePanel from "@/views/admin/PerUserUsagePanel";
|
||||
import SystemUsagePanel from "@/views/admin/SystemUsagePanel";
|
||||
@@ -26,7 +26,7 @@ export default function UsagePage() {
|
||||
description={t("page.description")}
|
||||
divider
|
||||
rightChildren={
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={timeRange}
|
||||
onValueChange={(value) => {
|
||||
if (!value) return;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { Card, DateRange, DateRangePicker, Text } from "@opal/components";
|
||||
import { Card, DateRange, InputDateRangePicker, Text } from "@opal/components";
|
||||
import { Section } from "@opal/layouts";
|
||||
import { useAgents } from "@/lib/agents/hooks";
|
||||
import AgentAvatar from "@/refresh-components/avatars/AgentAvatar";
|
||||
@@ -136,7 +136,7 @@ export function AgentStats({ agentId }: AgentStatsProps) {
|
||||
{/* sm:flex-row / sm:items-center / sm:justify-between have no Section equivalent, kept as a raw div */}
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Text font="heading-h2">{t("analytics.agentChart.title")}</Text>
|
||||
<DateRangePicker value={dateRange} onValueChange={setDateRange} />
|
||||
<InputDateRangePicker value={dateRange} onValueChange={setDateRange} />
|
||||
</div>
|
||||
|
||||
<Section
|
||||
|
||||
@@ -28,7 +28,7 @@ import { useState, useEffect, memo, JSX } from "react";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
InputCheckbox,
|
||||
InputDatePicker,
|
||||
InputTextArea,
|
||||
type InputTextAreaProps,
|
||||
@@ -771,7 +771,7 @@ export const BooleanFormField = memo(function BooleanFormField({
|
||||
removeIndent ? "me-2" : "mx-3"
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
aria-label={`${label
|
||||
.toLowerCase()
|
||||
.replace(" ", "-")}-checkbox`}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Button, Checkbox, Divider, Tooltip } from "@opal/components";
|
||||
import { Button, InputCheckbox, Divider, Tooltip } from "@opal/components";
|
||||
import {
|
||||
ConfigurableSources,
|
||||
CredentialFieldSpec,
|
||||
@@ -662,7 +662,7 @@ export function FederatedConnectorForm({
|
||||
<div key={fieldKey} className="space-y-2 w-full">
|
||||
{isBoolType ? (
|
||||
<div className="flex items-center gap-3 py-2">
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={
|
||||
formState.config[fieldKey] !== undefined
|
||||
? Boolean(formState.config[fieldKey])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DateRangePickerValue } from "@opal/components";
|
||||
import { InputDateRangePickerValue } from "@opal/components";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { FiCalendar, FiChevronDown, FiXCircle } from "react-icons/fi";
|
||||
import { CustomDropdown } from "../Dropdown";
|
||||
@@ -12,8 +12,8 @@ export function SearchDateRangeSelector({
|
||||
isHorizontal,
|
||||
className,
|
||||
}: {
|
||||
value: DateRangePickerValue | null;
|
||||
onValueChange: (value: DateRangePickerValue | null) => void;
|
||||
value: InputDateRangePickerValue | null;
|
||||
onValueChange: (value: InputDateRangePickerValue | null) => void;
|
||||
isHorizontal?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
|
||||
@@ -3,15 +3,15 @@ import { DocumentSetSummary, Tag, ValidSources } from "@/lib/types";
|
||||
import { SourceMetadata } from "@/lib/search/interfaces";
|
||||
import { FiBook, FiBookmark, FiMap, FiX } from "react-icons/fi";
|
||||
import { SearchDateRangeSelector } from "@/components/dateRangeSelectors/SearchDateRangeSelector";
|
||||
import { DateRangePickerValue } from "@opal/components";
|
||||
import { InputDateRangePickerValue } from "@opal/components";
|
||||
import { listSourceMetadata } from "@/lib/sources";
|
||||
import { SourceIcon } from "@/components/SourceIcon";
|
||||
import { FilterDropdown } from "@/components/search/filtering/FilterDropdown";
|
||||
|
||||
export interface SourceSelectorProps {
|
||||
timeRange: DateRangePickerValue | null;
|
||||
timeRange: InputDateRangePickerValue | null;
|
||||
setTimeRange: React.Dispatch<
|
||||
React.SetStateAction<DateRangePickerValue | null>
|
||||
React.SetStateAction<InputDateRangePickerValue | null>
|
||||
>;
|
||||
showDocSidebar?: boolean;
|
||||
selectedSources: SourceMetadata[];
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { ConfirmationModalLayout } from "@opal/layouts";
|
||||
import { Button } from "@opal/components";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { SvgAlertCircle } from "@opal/icons";
|
||||
interface MoveCustomAgentChatModalProps {
|
||||
@@ -37,7 +37,7 @@ export function MoveCustomAgentChatModal({
|
||||
})}
|
||||
</Text>
|
||||
<div className="flex items-center gap-1">
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
id="move-custom-agent-do-not-show"
|
||||
checked={doNotShowAgain}
|
||||
onCheckedChange={(checked) => setDoNotShowAgain(Boolean(checked))}
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { DateRangePickerValue } from "@opal/components";
|
||||
import { InputDateRangePickerValue } from "@opal/components";
|
||||
import { SourceMetadata } from "./search/interfaces";
|
||||
import {
|
||||
getProviderOverrideForAgent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DateRangePickerValue } from "@opal/components";
|
||||
import { InputDateRangePickerValue } from "@opal/components";
|
||||
import { Tag, ValidSources } from "../types";
|
||||
import { Agent } from "@/lib/agents/types";
|
||||
|
||||
@@ -159,7 +159,7 @@ export interface SearchRequestArgs {
|
||||
agentic?: boolean;
|
||||
sources: SourceMetadata[];
|
||||
documentSets: string[];
|
||||
timeRange: DateRangePickerValue | null;
|
||||
timeRange: InputDateRangePickerValue | null;
|
||||
tags: Tag[];
|
||||
persona: Agent;
|
||||
updateDocumentRelevance: (relevance: any) => void;
|
||||
|
||||
@@ -6,12 +6,14 @@ import { errorHandlingFetcher } from "@/lib/fetcher";
|
||||
import { SWR_KEYS } from "@/lib/swr-keys";
|
||||
import type { Tag, ValidSources } from "@/lib/types";
|
||||
import type { SourceMetadata } from "@/lib/search/interfaces";
|
||||
import type { DateRangePickerValue } from "@opal/components";
|
||||
import type { InputDateRangePickerValue } from "@opal/components";
|
||||
import { getConfiguredSources } from "@/lib/sources";
|
||||
import type { SearchFilters } from "@/lib/searchFilters/types";
|
||||
|
||||
export function useSearchFilters(): SearchFilters {
|
||||
const [timeRange, setTimeRange] = useState<DateRangePickerValue | null>(null);
|
||||
const [timeRange, setTimeRange] = useState<InputDateRangePickerValue | null>(
|
||||
null
|
||||
);
|
||||
const [selectedSources, setSelectedSources] = useState<SourceMetadata[]>([]);
|
||||
const [selectedDocumentSets, setSelectedDocumentSets] = useState<string[]>(
|
||||
[]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Tag } from "@/lib/types";
|
||||
import type { SourceMetadata } from "@/lib/search/interfaces";
|
||||
import type { DateRangePickerValue } from "@opal/components";
|
||||
import type { InputDateRangePickerValue } from "@opal/components";
|
||||
|
||||
/** What is selected, without the means to change it. */
|
||||
export interface SearchFiltersSelection {
|
||||
timeRange: DateRangePickerValue | null;
|
||||
timeRange: InputDateRangePickerValue | null;
|
||||
selectedSources: SourceMetadata[];
|
||||
selectedDocumentSets: string[];
|
||||
selectedTags: Tag[];
|
||||
@@ -13,7 +13,7 @@ export interface SearchFiltersSelection {
|
||||
/** The live selection a user edits: which sources, sets, tags and dates to search. */
|
||||
export interface SearchFilters extends SearchFiltersSelection {
|
||||
setTimeRange: React.Dispatch<
|
||||
React.SetStateAction<DateRangePickerValue | null>
|
||||
React.SetStateAction<InputDateRangePickerValue | null>
|
||||
>;
|
||||
setSelectedSources: React.Dispatch<React.SetStateAction<SourceMetadata[]>>;
|
||||
setSelectedDocumentSets: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { Tag } from "@/lib/types";
|
||||
import type { SourceMetadata } from "@/lib/search/interfaces";
|
||||
import type { DateRangePickerValue } from "@opal/components";
|
||||
import type { InputDateRangePickerValue } from "@opal/components";
|
||||
import type { SearchFiltersRequest } from "@/lib/searchFilters/types";
|
||||
|
||||
/** Freezes a live selection into the shape the backend receives. */
|
||||
export function buildFilters(
|
||||
sources: SourceMetadata[],
|
||||
documentSets: string[],
|
||||
timeRange: DateRangePickerValue | null,
|
||||
timeRange: InputDateRangePickerValue | null,
|
||||
tags: Tag[]
|
||||
): SearchFiltersRequest {
|
||||
return {
|
||||
|
||||
@@ -21,12 +21,12 @@ import type { SystemUsageResponse } from "@/lib/usage/systemUsage";
|
||||
import {
|
||||
THIRTY_DAYS,
|
||||
type DateRange,
|
||||
type DateRangePickerValue,
|
||||
type InputDateRangePickerValue,
|
||||
rangeForInclusiveDays,
|
||||
} from "@opal/components";
|
||||
|
||||
export function useTimeRange() {
|
||||
return useState<DateRangePickerValue>({
|
||||
return useState<InputDateRangePickerValue>({
|
||||
...rangeForInclusiveDays(30),
|
||||
selectValue: THIRTY_DAYS,
|
||||
});
|
||||
@@ -46,14 +46,14 @@ export function useSystemUsage(range?: DateRange) {
|
||||
return { usage: data, isLoading, error, refetch: mutate };
|
||||
}
|
||||
|
||||
function analyticsRange(timeRange: DateRangePickerValue) {
|
||||
function analyticsRange(timeRange: InputDateRangePickerValue) {
|
||||
return {
|
||||
start: convertDateToStartOfDay(timeRange.from)?.toISOString(),
|
||||
end: convertDateToEndOfDay(timeRange.to)?.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
export function useQueryAnalytics(timeRange: DateRangePickerValue) {
|
||||
export function useQueryAnalytics(timeRange: InputDateRangePickerValue) {
|
||||
const url = buildApiPath(
|
||||
"/api/analytics/admin/query",
|
||||
analyticsRange(timeRange)
|
||||
@@ -66,7 +66,7 @@ export function useQueryAnalytics(timeRange: DateRangePickerValue) {
|
||||
};
|
||||
}
|
||||
|
||||
export function useUserAnalytics(timeRange: DateRangePickerValue) {
|
||||
export function useUserAnalytics(timeRange: InputDateRangePickerValue) {
|
||||
const url = buildApiPath(
|
||||
"/api/analytics/admin/user",
|
||||
analyticsRange(timeRange)
|
||||
@@ -79,7 +79,7 @@ export function useUserAnalytics(timeRange: DateRangePickerValue) {
|
||||
};
|
||||
}
|
||||
|
||||
export function useOnyxBotAnalytics(timeRange: DateRangePickerValue) {
|
||||
export function useOnyxBotAnalytics(timeRange: InputDateRangePickerValue) {
|
||||
const url = buildApiPath(
|
||||
"/api/analytics/admin/onyxbot",
|
||||
analyticsRange(timeRange)
|
||||
@@ -94,7 +94,7 @@ export function useOnyxBotAnalytics(timeRange: DateRangePickerValue) {
|
||||
|
||||
export function usePersonaMessages(
|
||||
personaId: number | undefined,
|
||||
timeRange: DateRangePickerValue
|
||||
timeRange: InputDateRangePickerValue
|
||||
) {
|
||||
const url = buildApiPath("/api/analytics/admin/persona/messages", {
|
||||
persona_id: personaId?.toString(),
|
||||
@@ -116,7 +116,7 @@ export function usePersonaMessages(
|
||||
|
||||
export function usePersonaUniqueUsers(
|
||||
personaId: number | undefined,
|
||||
timeRange: DateRangePickerValue
|
||||
timeRange: InputDateRangePickerValue
|
||||
) {
|
||||
const url = buildApiPath("/api/analytics/admin/persona/unique-users", {
|
||||
persona_id: personaId?.toString(),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useField } from "formik";
|
||||
import { Checkbox, type CheckboxProps } from "@opal/components";
|
||||
import { InputCheckbox, type InputCheckboxProps } from "@opal/components";
|
||||
import { useOnChangeValue } from "@/hooks/formHooks";
|
||||
|
||||
interface CheckboxFieldProps extends Omit<CheckboxProps, "checked"> {
|
||||
interface CheckboxFieldProps extends Omit<InputCheckboxProps, "checked"> {
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ export default function UnlabeledCheckboxField({
|
||||
const onChange = useOnChangeValue(name, onCheckedChange);
|
||||
|
||||
return (
|
||||
<Checkbox checked={field.value} onCheckedChange={onChange} {...props} />
|
||||
<InputCheckbox
|
||||
checked={field.value}
|
||||
onCheckedChange={onChange}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ type Story = StoryObj;
|
||||
// CheckboxField (unlabeled)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const Checkbox: Story = {
|
||||
export const InputCheckbox: Story = {
|
||||
name: "CheckboxField (unlabeled)",
|
||||
render: () => (
|
||||
<FormikWrapper initialValues={{ agree: false }}>
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from "react";
|
||||
import { useField } from "formik";
|
||||
import { cn } from "@opal/utils";
|
||||
import { Tooltip } from "@opal/components";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
|
||||
interface CheckboxFieldProps {
|
||||
name: string;
|
||||
@@ -40,7 +40,7 @@ export const CheckboxField: React.FC<CheckboxFieldProps> = ({
|
||||
|
||||
const checkboxContent = (
|
||||
<div className="flex w-fit items-start space-x-2">
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
id={name}
|
||||
aria-labelledby={labelId}
|
||||
checked={field.value}
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as GeneralLayouts from "@/layouts/general-layouts";
|
||||
import * as TableLayouts from "@/layouts/table-layouts";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
InputCheckbox,
|
||||
CopyButton,
|
||||
Divider as OpalDivider,
|
||||
InputTypeIn,
|
||||
@@ -721,7 +721,7 @@ export default function SourceHierarchyBrowser({
|
||||
return <SvgFolder size={16} />;
|
||||
}
|
||||
if (isSelected) {
|
||||
return <Checkbox checked={true} />;
|
||||
return <InputCheckbox checked={true} />;
|
||||
}
|
||||
return <SvgFileText size={16} />;
|
||||
};
|
||||
@@ -803,7 +803,7 @@ export default function SourceHierarchyBrowser({
|
||||
<TableLayouts.TableRow>
|
||||
<TableLayouts.CheckboxCell>
|
||||
{filteredItems.length > 0 && (
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={allVisibleSelected}
|
||||
indeterminate={someVisibleSelected}
|
||||
onCheckedChange={handleHeaderCheckboxClick}
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { SearchDocWithContent } from "@/lib/search/interfaces";
|
||||
import type { ValidSources } from "@/lib/types";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
InputCheckbox,
|
||||
Divider,
|
||||
InputTypeIn,
|
||||
LineItemButton,
|
||||
@@ -305,7 +305,7 @@ export function KnowledgeSearchResultsPanel({
|
||||
aria-label={`search-node-${node.id}`}
|
||||
>
|
||||
<TableLayouts.CheckboxCell>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={isSelected}
|
||||
onCheckedChange={() => onToggleFolder(node.id)}
|
||||
/>
|
||||
@@ -370,7 +370,7 @@ export function KnowledgeSearchResultsPanel({
|
||||
aria-label={`search-doc-${doc.document_id}`}
|
||||
>
|
||||
<TableLayouts.CheckboxCell>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={isSelected}
|
||||
onCheckedChange={() => onToggleDocument(doc.document_id)}
|
||||
/>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
|
||||
import * as GeneralLayouts from "@/layouts/general-layouts";
|
||||
import * as TableLayouts from "@/layouts/table-layouts";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { Checkbox, Divider, InputTypeIn, Spacer } from "@opal/components";
|
||||
import { InputCheckbox, Divider, InputTypeIn, Spacer } from "@opal/components";
|
||||
import { SvgFilter } from "@opal/icons";
|
||||
|
||||
export interface KnowledgeTableColumn<T> {
|
||||
@@ -117,7 +117,7 @@ export function KnowledgeTable<T>({
|
||||
}
|
||||
>
|
||||
<TableLayouts.CheckboxCell>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={isSelected}
|
||||
onCheckedChange={() => onToggleItem(id)}
|
||||
/>
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
LLMProviderView,
|
||||
ModelConfiguration,
|
||||
} from "@/lib/languageModels/types";
|
||||
import { Checkbox } from "@opal/components";
|
||||
import { InputCheckbox } from "@opal/components";
|
||||
import InputTypeInField from "@/refresh-components/form/InputTypeInField";
|
||||
import { InputTypeIn } from "@opal/components";
|
||||
import { InputComboBox } from "@opal/components";
|
||||
@@ -641,7 +641,7 @@ function ModelRow({
|
||||
variant="section"
|
||||
sizePreset="main-ui"
|
||||
center
|
||||
icon={() => <Checkbox checked={isSelected} />}
|
||||
icon={() => <InputCheckbox checked={isSelected} />}
|
||||
title={displayName}
|
||||
description={buildModelDescription(model)}
|
||||
rightChildren={
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Route } from "next";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
InputCheckbox,
|
||||
InputTypeIn,
|
||||
MessageCard,
|
||||
Modal,
|
||||
@@ -387,7 +387,7 @@ export function ImportSkillsFromGitHubModalView({
|
||||
{preview && (
|
||||
<div className="flex min-h-0 flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={allImportableSelected}
|
||||
indeterminate={
|
||||
selectedPaths.length > 0 && !allImportableSelected
|
||||
@@ -418,7 +418,7 @@ export function ImportSkillsFromGitHubModalView({
|
||||
unavailable && "opacity-50"
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={checked}
|
||||
disabled={loading || unavailable}
|
||||
aria-label={t(
|
||||
|
||||
@@ -56,7 +56,7 @@ import {
|
||||
AttachmentItemButton,
|
||||
Button,
|
||||
Divider,
|
||||
Checkbox,
|
||||
InputCheckbox,
|
||||
Text,
|
||||
} from "@opal/components";
|
||||
import useFederatedOAuthStatus from "@/hooks/useFederatedOAuthStatus";
|
||||
@@ -204,7 +204,7 @@ function ScopeSelector({
|
||||
const locked = lockReason !== undefined;
|
||||
return (
|
||||
<div key={option.scope} className="flex items-start gap-2 ps-2">
|
||||
<Checkbox
|
||||
<InputCheckbox
|
||||
checked={selectedScopes.includes(option.scope) || locked}
|
||||
disabled={disabled || locked}
|
||||
onCheckedChange={() => toggleScope(option.scope)}
|
||||
|
||||
@@ -10,11 +10,11 @@ import {
|
||||
resolveChartState,
|
||||
useLoggedChartError,
|
||||
} from "@/sections/usage/AnalyticsChart";
|
||||
import { DateRangePickerValue } from "@opal/components";
|
||||
import { InputDateRangePickerValue } from "@opal/components";
|
||||
import { formatTokenCount } from "@/lib/format";
|
||||
|
||||
interface TimeRangeProps {
|
||||
timeRange: DateRangePickerValue;
|
||||
timeRange: InputDateRangePickerValue;
|
||||
}
|
||||
|
||||
export function UsageChart({ timeRange }: TimeRangeProps) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
chartSeries,
|
||||
resolveChartState,
|
||||
} from "@/sections/usage/AnalyticsChart";
|
||||
import { DateRangePickerValue } from "@opal/components";
|
||||
import { InputDateRangePickerValue } from "@opal/components";
|
||||
import { Agent } from "@/lib/agents/types";
|
||||
|
||||
interface PersonaPickerProps {
|
||||
@@ -107,7 +107,7 @@ function PersonaPicker({
|
||||
}
|
||||
|
||||
interface PersonaMessagesChartProps {
|
||||
timeRange: DateRangePickerValue;
|
||||
timeRange: InputDateRangePickerValue;
|
||||
}
|
||||
|
||||
export function PersonaMessagesChart({ timeRange }: PersonaMessagesChartProps) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useAdminRouteTitle } from "@/lib/adminNavLabels";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { DateRangePicker } from "@opal/components";
|
||||
import { InputDateRangePicker } from "@opal/components";
|
||||
import { useTimeRange } from "@/lib/usage/hooks";
|
||||
import {
|
||||
FeedbackChart,
|
||||
@@ -30,7 +30,7 @@ export default function WorkspaceAnalyticsPage() {
|
||||
description={t("page.description")}
|
||||
divider
|
||||
rightChildren={
|
||||
<DateRangePicker
|
||||
<InputDateRangePicker
|
||||
value={timeRange}
|
||||
onValueChange={(range) =>
|
||||
setTimeRange((previous) =>
|
||||
|
||||
Reference in new issue
Block a user