@pcoi/components 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/index.d.ts +826 -568
- package/dist/index.js +3517 -2
- package/dist/styles.css +2 -0
- package/package.json +36 -32
- package/dist/components.css +0 -1
- package/dist/index.mjs +0 -969
- package/src/Badge/Badge.css +0 -40
- package/src/Badge/Badge.tsx +0 -36
- package/src/Badge/index.ts +0 -2
- package/src/Button/Button.css +0 -93
- package/src/Button/Button.figma.tsx +0 -27
- package/src/Button/Button.test.tsx +0 -32
- package/src/Button/Button.tsx +0 -47
- package/src/Button/index.ts +0 -1
- package/src/Callout/Callout.css +0 -43
- package/src/Callout/Callout.figma.tsx +0 -25
- package/src/Callout/Callout.tsx +0 -39
- package/src/Callout/index.ts +0 -1
- package/src/Card/Card.css +0 -88
- package/src/Card/Card.figma.tsx +0 -28
- package/src/Card/Card.tsx +0 -60
- package/src/Card/index.ts +0 -1
- package/src/ChatInterface/ChatInterface.css +0 -49
- package/src/ChatInterface/ChatInterface.integration.test.tsx +0 -123
- package/src/ChatInterface/ChatInterface.tsx +0 -120
- package/src/ChatInterface/index.ts +0 -6
- package/src/ChatMessage/ChatMessage.css +0 -55
- package/src/ChatMessage/ChatMessage.tsx +0 -71
- package/src/ChatMessage/index.ts +0 -2
- package/src/ChatMessageList/ChatMessageList.css +0 -24
- package/src/ChatMessageList/ChatMessageList.test.tsx +0 -74
- package/src/ChatMessageList/ChatMessageList.tsx +0 -51
- package/src/ChatMessageList/index.ts +0 -2
- package/src/Checkbox/Checkbox.css +0 -97
- package/src/Checkbox/Checkbox.tsx +0 -70
- package/src/Checkbox/index.ts +0 -2
- package/src/CitationMark/CitationMark.css +0 -40
- package/src/CitationMark/CitationMark.tsx +0 -38
- package/src/CitationMark/index.ts +0 -2
- package/src/CitedExcerpt/CitedExcerpt.css +0 -75
- package/src/CitedExcerpt/CitedExcerpt.tsx +0 -51
- package/src/CitedExcerpt/index.ts +0 -2
- package/src/ComparisonTable/ComparisonTable.css +0 -66
- package/src/ComparisonTable/ComparisonTable.tsx +0 -48
- package/src/ComparisonTable/index.ts +0 -1
- package/src/ContactForm/ContactForm.css +0 -38
- package/src/ContactForm/ContactForm.tsx +0 -57
- package/src/ContactForm/index.ts +0 -1
- package/src/DataTable/DataTable.css +0 -56
- package/src/DataTable/DataTable.tsx +0 -104
- package/src/DataTable/index.ts +0 -2
- package/src/DocumentOverlay/DocumentOverlay.css +0 -57
- package/src/DocumentOverlay/DocumentOverlay.test.tsx +0 -95
- package/src/DocumentOverlay/DocumentOverlay.tsx +0 -86
- package/src/DocumentOverlay/index.ts +0 -2
- package/src/Footer/Footer.css +0 -72
- package/src/Footer/Footer.tsx +0 -56
- package/src/Footer/index.ts +0 -1
- package/src/FormField/FormField.css +0 -78
- package/src/FormField/FormField.figma.tsx +0 -28
- package/src/FormField/FormField.tsx +0 -103
- package/src/FormField/index.ts +0 -2
- package/src/HowStep/HowStep.css +0 -48
- package/src/HowStep/HowStep.figma.tsx +0 -23
- package/src/HowStep/HowStep.tsx +0 -38
- package/src/HowStep/index.ts +0 -1
- package/src/LogoMark/LogoMark.css +0 -16
- package/src/LogoMark/LogoMark.tsx +0 -24
- package/src/LogoMark/index.ts +0 -2
- package/src/Modal/Modal.css +0 -101
- package/src/Modal/Modal.figma.tsx +0 -28
- package/src/Modal/Modal.test.tsx +0 -46
- package/src/Modal/Modal.tsx +0 -139
- package/src/Modal/index.ts +0 -2
- package/src/Nav/Nav.css +0 -161
- package/src/Nav/Nav.tsx +0 -101
- package/src/Nav/index.ts +0 -1
- package/src/Panel/Panel.css +0 -35
- package/src/Panel/Panel.tsx +0 -61
- package/src/Panel/index.ts +0 -2
- package/src/PromptBar/PromptBar.css +0 -68
- package/src/PromptBar/PromptBar.figma.tsx +0 -25
- package/src/PromptBar/PromptBar.test.tsx +0 -83
- package/src/PromptBar/PromptBar.tsx +0 -93
- package/src/PromptBar/index.ts +0 -2
- package/src/RadioGroup/RadioGroup.css +0 -117
- package/src/RadioGroup/RadioGroup.tsx +0 -112
- package/src/RadioGroup/index.ts +0 -2
- package/src/SectionHeader/SectionHeader.css +0 -38
- package/src/SectionHeader/SectionHeader.figma.tsx +0 -23
- package/src/SectionHeader/SectionHeader.tsx +0 -55
- package/src/SectionHeader/index.ts +0 -1
- package/src/Select/Select.css +0 -90
- package/src/Select/Select.figma.tsx +0 -33
- package/src/Select/Select.tsx +0 -100
- package/src/Select/index.ts +0 -2
- package/src/SignalsPanel/SignalsPanel.css +0 -51
- package/src/SignalsPanel/SignalsPanel.tsx +0 -33
- package/src/SignalsPanel/index.ts +0 -1
- package/src/SuggestionCard/SuggestionCard.css +0 -51
- package/src/SuggestionCard/SuggestionCard.tsx +0 -34
- package/src/SuggestionCard/index.ts +0 -2
- package/src/SuggestionCards/SuggestionCards.css +0 -15
- package/src/SuggestionCards/SuggestionCards.test.tsx +0 -27
- package/src/SuggestionCards/SuggestionCards.tsx +0 -40
- package/src/SuggestionCards/index.ts +0 -2
- package/src/Toast/Toast.css +0 -85
- package/src/Toast/Toast.tsx +0 -77
- package/src/Toast/index.ts +0 -2
- package/src/Toggle/Toggle.css +0 -110
- package/src/Toggle/Toggle.figma.tsx +0 -24
- package/src/Toggle/Toggle.tsx +0 -73
- package/src/Toggle/index.ts +0 -2
- package/src/TypingIndicator/TypingIndicator.css +0 -70
- package/src/TypingIndicator/TypingIndicator.tsx +0 -37
- package/src/TypingIndicator/index.ts +0 -2
- package/src/index.ts +0 -37
- package/src/styles/utilities.css +0 -14
- package/src/styles.css +0 -32
- package/src/types.ts +0 -65
package/src/Panel/Panel.css
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/* Panel — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-panel {
|
|
4
|
-
background: var(--pcoi-semantic-bg-card);
|
|
5
|
-
border: 1px solid var(--pcoi-semantic-border-default);
|
|
6
|
-
border-radius: var(--pcoi-semantic-radius-card);
|
|
7
|
-
overflow: hidden;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.pcoi-panel__header {
|
|
11
|
-
display: flex;
|
|
12
|
-
align-items: center;
|
|
13
|
-
justify-content: space-between;
|
|
14
|
-
padding: var(--pcoi-semantic-spacing-panel-padding-sm) var(--pcoi-semantic-spacing-panel-padding);
|
|
15
|
-
border-bottom: 1px solid var(--pcoi-semantic-border-default);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.pcoi-panel__title {
|
|
19
|
-
font-size: var(--pcoi-semantic-type-body-size);
|
|
20
|
-
font-weight: var(--pcoi-semantic-type-emphasis-weight);
|
|
21
|
-
color: var(--pcoi-semantic-text-primary);
|
|
22
|
-
margin: 0;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.pcoi-panel__action {
|
|
26
|
-
flex-shrink: 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.pcoi-panel__body {
|
|
30
|
-
padding: var(--pcoi-semantic-spacing-panel-padding);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.pcoi-panel--flush .pcoi-panel__body {
|
|
34
|
-
padding: 0;
|
|
35
|
-
}
|
package/src/Panel/Panel.tsx
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { HeadingLevel } from "../types";
|
|
3
|
-
|
|
4
|
-
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
/** Panel title rendered in a header bar */
|
|
6
|
-
title?: string;
|
|
7
|
-
/** Heading level for the title (default: "h3") */
|
|
8
|
-
headingLevel?: HeadingLevel;
|
|
9
|
-
/** Action slot in the header (e.g. a Button or filter) */
|
|
10
|
-
action?: React.ReactNode;
|
|
11
|
-
/** Panel body content */
|
|
12
|
-
children: React.ReactNode;
|
|
13
|
-
/** Remove default body padding (for tables/charts that need edge-to-edge content) */
|
|
14
|
-
flush?: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* PCOI Panel — Dashboard section container with optional header
|
|
19
|
-
* Tokens: bg/card, border/default, radius-md, spacing-16/24, text/primary
|
|
20
|
-
*/
|
|
21
|
-
export const Panel = React.forwardRef<HTMLDivElement, PanelProps>(
|
|
22
|
-
(
|
|
23
|
-
{
|
|
24
|
-
title,
|
|
25
|
-
headingLevel = "h3",
|
|
26
|
-
action,
|
|
27
|
-
children,
|
|
28
|
-
flush = false,
|
|
29
|
-
className = "",
|
|
30
|
-
...rest
|
|
31
|
-
},
|
|
32
|
-
ref
|
|
33
|
-
) => {
|
|
34
|
-
const Heading = headingLevel;
|
|
35
|
-
|
|
36
|
-
const wrapperClasses = [
|
|
37
|
-
"pcoi-panel",
|
|
38
|
-
flush ? "pcoi-panel--flush" : "",
|
|
39
|
-
className,
|
|
40
|
-
]
|
|
41
|
-
.filter(Boolean)
|
|
42
|
-
.join(" ");
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<div ref={ref} className={wrapperClasses} {...rest}>
|
|
46
|
-
{(title || action) && (
|
|
47
|
-
<div className="pcoi-panel__header">
|
|
48
|
-
{title && (
|
|
49
|
-
<Heading className="pcoi-panel__title">{title}</Heading>
|
|
50
|
-
)}
|
|
51
|
-
{action && <div className="pcoi-panel__action">{action}</div>}
|
|
52
|
-
</div>
|
|
53
|
-
)}
|
|
54
|
-
<div className="pcoi-panel__body">{children}</div>
|
|
55
|
-
</div>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
Panel.displayName = "Panel";
|
|
61
|
-
export default Panel;
|
package/src/Panel/index.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/* PromptBar — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-prompt-bar {
|
|
4
|
-
display: flex;
|
|
5
|
-
align-items: flex-end;
|
|
6
|
-
gap: var(--pcoi-semantic-spacing-panel-gap);
|
|
7
|
-
padding: var(--pcoi-semantic-spacing-input-y) var(--pcoi-semantic-spacing-input-x);
|
|
8
|
-
background: var(--pcoi-semantic-bg-card);
|
|
9
|
-
border: 1px solid var(--pcoi-semantic-border-default);
|
|
10
|
-
border-radius: var(--pcoi-semantic-radius-card);
|
|
11
|
-
transition:
|
|
12
|
-
border-color var(--pcoi-effect-transition-fast),
|
|
13
|
-
box-shadow var(--pcoi-effect-transition-fast);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.pcoi-prompt-bar:not(:focus-within):hover {
|
|
17
|
-
border-color: var(--pcoi-semantic-border-input-hover);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.pcoi-prompt-bar:focus-within {
|
|
21
|
-
border-color: var(--pcoi-semantic-focus-border);
|
|
22
|
-
box-shadow: var(--pcoi-effect-shadow-focus-ring);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.pcoi-prompt-bar__input {
|
|
26
|
-
flex: 1;
|
|
27
|
-
min-height: 24px;
|
|
28
|
-
max-height: 120px;
|
|
29
|
-
padding: var(--pcoi-semantic-spacing-control-padding-2xs) 0;
|
|
30
|
-
font-family: var(--pcoi-semantic-type-body-font);
|
|
31
|
-
font-size: var(--pcoi-semantic-type-body-size);
|
|
32
|
-
line-height: var(--pcoi-semantic-type-body-line-height);
|
|
33
|
-
color: var(--pcoi-semantic-text-primary);
|
|
34
|
-
background: transparent;
|
|
35
|
-
border: none;
|
|
36
|
-
outline: none;
|
|
37
|
-
resize: none;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.pcoi-prompt-bar__input::placeholder {
|
|
41
|
-
color: var(--pcoi-semantic-text-muted);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.pcoi-prompt-bar__actions {
|
|
45
|
-
flex-shrink: 0;
|
|
46
|
-
display: flex;
|
|
47
|
-
align-items: center;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.pcoi-prompt-bar__send.pcoi-btn {
|
|
51
|
-
padding: var(--pcoi-semantic-spacing-control-padding-xs);
|
|
52
|
-
min-width: 0;
|
|
53
|
-
min-height: 0;
|
|
54
|
-
width: var(--pcoi-semantic-sizing-btn-height-nav);
|
|
55
|
-
height: var(--pcoi-semantic-sizing-btn-height-nav);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.pcoi-prompt-bar__send-icon {
|
|
59
|
-
display: flex;
|
|
60
|
-
transform: rotate(-90deg);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* ── Responsive: compact send on small mobile ── */
|
|
64
|
-
@media (max-width: 480px) {
|
|
65
|
-
.pcoi-prompt-bar {
|
|
66
|
-
padding: var(--pcoi-semantic-spacing-control-padding-sm) var(--pcoi-semantic-spacing-input-y);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import figma from "@figma/code-connect";
|
|
2
|
-
import { PromptBar } from "./PromptBar";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Code Connect: PromptBar
|
|
6
|
-
* Maps Figma prompt bar states to React PromptBar props
|
|
7
|
-
*/
|
|
8
|
-
figma.connect(PromptBar, "https://www.figma.com/file/PCOIxDesignSystem/PCOI-Design-System?node-id=105-1", {
|
|
9
|
-
props: {
|
|
10
|
-
value: figma.string("Value"),
|
|
11
|
-
placeholder: figma.string("Placeholder"),
|
|
12
|
-
disabled: figma.boolean("Disabled"),
|
|
13
|
-
loading: figma.boolean("Loading"),
|
|
14
|
-
},
|
|
15
|
-
example: ({ value, placeholder, disabled, loading }) => (
|
|
16
|
-
<PromptBar
|
|
17
|
-
value={value}
|
|
18
|
-
placeholder={placeholder}
|
|
19
|
-
disabled={disabled}
|
|
20
|
-
loading={loading}
|
|
21
|
-
onChange={() => undefined}
|
|
22
|
-
onSubmit={() => undefined}
|
|
23
|
-
/>
|
|
24
|
-
),
|
|
25
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { PromptBar } from "./PromptBar";
|
|
4
|
-
|
|
5
|
-
describe("PromptBar", () => {
|
|
6
|
-
it("calls onChange with textarea value", () => {
|
|
7
|
-
const onChange = vi.fn();
|
|
8
|
-
const onSubmit = vi.fn();
|
|
9
|
-
|
|
10
|
-
render(
|
|
11
|
-
<PromptBar
|
|
12
|
-
value=""
|
|
13
|
-
onChange={onChange}
|
|
14
|
-
onSubmit={onSubmit}
|
|
15
|
-
placeholder="Ask"
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
fireEvent.change(screen.getByRole("textbox", { name: "Ask" }), {
|
|
20
|
-
target: { value: "hello" },
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
expect(onChange).toHaveBeenCalledWith("hello");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("submits trimmed value on Enter", () => {
|
|
27
|
-
const onChange = vi.fn();
|
|
28
|
-
const onSubmit = vi.fn();
|
|
29
|
-
|
|
30
|
-
render(
|
|
31
|
-
<PromptBar
|
|
32
|
-
value=" hello world "
|
|
33
|
-
onChange={onChange}
|
|
34
|
-
onSubmit={onSubmit}
|
|
35
|
-
placeholder="Ask"
|
|
36
|
-
/>
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
fireEvent.keyDown(screen.getByRole("textbox", { name: "Ask" }), {
|
|
40
|
-
key: "Enter",
|
|
41
|
-
code: "Enter",
|
|
42
|
-
shiftKey: false,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(onSubmit).toHaveBeenCalledTimes(1);
|
|
46
|
-
expect(onSubmit).toHaveBeenCalledWith("hello world");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("does not submit on Shift+Enter", () => {
|
|
50
|
-
const onChange = vi.fn();
|
|
51
|
-
const onSubmit = vi.fn();
|
|
52
|
-
|
|
53
|
-
render(
|
|
54
|
-
<PromptBar
|
|
55
|
-
value="hello"
|
|
56
|
-
onChange={onChange}
|
|
57
|
-
onSubmit={onSubmit}
|
|
58
|
-
placeholder="Ask"
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
fireEvent.keyDown(screen.getByRole("textbox", { name: "Ask" }), {
|
|
63
|
-
key: "Enter",
|
|
64
|
-
code: "Enter",
|
|
65
|
-
shiftKey: true,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(onSubmit).not.toHaveBeenCalled();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("disables send button when value is empty/whitespace", () => {
|
|
72
|
-
render(
|
|
73
|
-
<PromptBar
|
|
74
|
-
value=" "
|
|
75
|
-
onChange={vi.fn()}
|
|
76
|
-
onSubmit={vi.fn()}
|
|
77
|
-
placeholder="Ask"
|
|
78
|
-
/>
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
expect(screen.getByRole("button", { name: "Send message" })).toBeDisabled();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import React, { useRef, useCallback } from "react";
|
|
2
|
-
import { Button } from "../Button/Button";
|
|
3
|
-
import { ArrowRightIcon } from "@pcoi/icons";
|
|
4
|
-
|
|
5
|
-
export interface PromptBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "onSubmit"> {
|
|
6
|
-
/** Current textarea value */
|
|
7
|
-
value: string;
|
|
8
|
-
/** Called when the textarea value changes */
|
|
9
|
-
onChange: (value: string) => void;
|
|
10
|
-
/** Called when the user submits (Enter or send button) */
|
|
11
|
-
onSubmit: (value: string) => void;
|
|
12
|
-
/** Placeholder text */
|
|
13
|
-
placeholder?: string;
|
|
14
|
-
/** Disable input and send */
|
|
15
|
-
disabled?: boolean;
|
|
16
|
-
/** Show loading state on send button */
|
|
17
|
-
loading?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* PCOI PromptBar — Textarea + send button for chat input
|
|
22
|
-
* Tokens: bg/card, border/default, focus-border, radius-md,
|
|
23
|
-
* text/primary, text/muted, transition-fast
|
|
24
|
-
*/
|
|
25
|
-
export const PromptBar = React.forwardRef<HTMLDivElement, PromptBarProps>(
|
|
26
|
-
(
|
|
27
|
-
{
|
|
28
|
-
value,
|
|
29
|
-
onChange,
|
|
30
|
-
onSubmit,
|
|
31
|
-
placeholder = "Ask a question…",
|
|
32
|
-
disabled = false,
|
|
33
|
-
loading = false,
|
|
34
|
-
className = "",
|
|
35
|
-
...rest
|
|
36
|
-
},
|
|
37
|
-
ref
|
|
38
|
-
) => {
|
|
39
|
-
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
40
|
-
|
|
41
|
-
const handleKeyDown = useCallback(
|
|
42
|
-
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
43
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
44
|
-
e.preventDefault();
|
|
45
|
-
if (value.trim() && !disabled && !loading) {
|
|
46
|
-
onSubmit(value.trim());
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
[value, disabled, loading, onSubmit]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const handleSubmit = useCallback(() => {
|
|
54
|
-
if (value.trim() && !disabled && !loading) {
|
|
55
|
-
onSubmit(value.trim());
|
|
56
|
-
}
|
|
57
|
-
}, [value, disabled, loading, onSubmit]);
|
|
58
|
-
|
|
59
|
-
const classes = ["pcoi-prompt-bar", className].filter(Boolean).join(" ");
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div ref={ref} className={classes} {...rest}>
|
|
63
|
-
<textarea
|
|
64
|
-
ref={textareaRef}
|
|
65
|
-
className="pcoi-prompt-bar__input"
|
|
66
|
-
value={value}
|
|
67
|
-
onChange={(e) => onChange(e.target.value)}
|
|
68
|
-
onKeyDown={handleKeyDown}
|
|
69
|
-
placeholder={placeholder}
|
|
70
|
-
disabled={disabled}
|
|
71
|
-
rows={1}
|
|
72
|
-
aria-label={placeholder}
|
|
73
|
-
/>
|
|
74
|
-
<div className="pcoi-prompt-bar__actions">
|
|
75
|
-
<Button
|
|
76
|
-
variant="primary"
|
|
77
|
-
className="pcoi-prompt-bar__send"
|
|
78
|
-
onClick={handleSubmit}
|
|
79
|
-
disabled={disabled || loading || !value.trim()}
|
|
80
|
-
aria-label="Send message"
|
|
81
|
-
>
|
|
82
|
-
<span className="pcoi-prompt-bar__send-icon">
|
|
83
|
-
<ArrowRightIcon size={16} />
|
|
84
|
-
</span>
|
|
85
|
-
</Button>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
PromptBar.displayName = "PromptBar";
|
|
93
|
-
export default PromptBar;
|
package/src/PromptBar/index.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/* RadioGroup — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-radio-group {
|
|
4
|
-
border: none;
|
|
5
|
-
padding: 0;
|
|
6
|
-
margin: 0;
|
|
7
|
-
display: flex;
|
|
8
|
-
flex-direction: column;
|
|
9
|
-
gap: var(--pcoi-semantic-spacing-text-gap-sm);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.pcoi-radio-group__legend {
|
|
13
|
-
font-size: var(--pcoi-semantic-type-label-size);
|
|
14
|
-
font-weight: var(--pcoi-semantic-type-label-weight);
|
|
15
|
-
letter-spacing: var(--pcoi-semantic-type-label-letter-spacing);
|
|
16
|
-
text-transform: uppercase;
|
|
17
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
18
|
-
margin-bottom: var(--pcoi-semantic-spacing-inline-2xs);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.pcoi-radio-group__required {
|
|
22
|
-
color: var(--pcoi-semantic-text-error);
|
|
23
|
-
margin-left: var(--pcoi-semantic-spacing-inline-2xs);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.pcoi-radio-group__options {
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-direction: column;
|
|
29
|
-
gap: var(--pcoi-semantic-spacing-panel-gap);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.pcoi-radio-group__option {
|
|
33
|
-
display: inline-flex;
|
|
34
|
-
align-items: center;
|
|
35
|
-
gap: var(--pcoi-semantic-spacing-inline-sm);
|
|
36
|
-
cursor: pointer;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.pcoi-radio-group__input {
|
|
40
|
-
position: absolute;
|
|
41
|
-
width: 1px;
|
|
42
|
-
height: 1px;
|
|
43
|
-
padding: 0;
|
|
44
|
-
margin: -1px;
|
|
45
|
-
overflow: hidden;
|
|
46
|
-
clip: rect(0, 0, 0, 0);
|
|
47
|
-
white-space: nowrap;
|
|
48
|
-
border-width: 0;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.pcoi-radio-group__circle {
|
|
52
|
-
width: var(--pcoi-semantic-sizing-control-box-size);
|
|
53
|
-
height: var(--pcoi-semantic-sizing-control-box-size);
|
|
54
|
-
flex-shrink: 0;
|
|
55
|
-
border: 1px solid var(--pcoi-semantic-border-default);
|
|
56
|
-
border-radius: var(--pcoi-semantic-radius-badge);
|
|
57
|
-
background: var(--pcoi-semantic-bg-default);
|
|
58
|
-
position: relative;
|
|
59
|
-
transition: border-color var(--pcoi-effect-transition-fast, 0.2s ease);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.pcoi-radio-group__circle::after {
|
|
63
|
-
content: "";
|
|
64
|
-
position: absolute;
|
|
65
|
-
top: 50%;
|
|
66
|
-
left: 50%;
|
|
67
|
-
transform: translate(-50%, -50%);
|
|
68
|
-
width: var(--pcoi-semantic-sizing-radio-dot-size);
|
|
69
|
-
height: var(--pcoi-semantic-sizing-radio-dot-size);
|
|
70
|
-
border-radius: var(--pcoi-semantic-radius-badge);
|
|
71
|
-
background: var(--pcoi-semantic-action-primary-bg);
|
|
72
|
-
opacity: 0;
|
|
73
|
-
transition: opacity var(--pcoi-effect-transition-fast, 0.2s ease);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.pcoi-radio-group__input:checked + .pcoi-radio-group__circle {
|
|
77
|
-
border-color: var(--pcoi-semantic-action-primary-bg);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.pcoi-radio-group__input:checked + .pcoi-radio-group__circle::after {
|
|
81
|
-
opacity: 1;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.pcoi-radio-group__option:hover .pcoi-radio-group__circle {
|
|
85
|
-
border-color: var(--pcoi-semantic-border-input-hover);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.pcoi-radio-group__option:active .pcoi-radio-group__circle {
|
|
89
|
-
transform: var(--pcoi-effect-transform-press-scale-control);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.pcoi-radio-group__input:focus-visible + .pcoi-radio-group__circle {
|
|
93
|
-
border-color: var(--pcoi-semantic-focus-border);
|
|
94
|
-
box-shadow: var(--pcoi-effect-shadow-focus-ring);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.pcoi-radio-group__label {
|
|
98
|
-
font-size: var(--pcoi-semantic-type-body-size);
|
|
99
|
-
color: var(--pcoi-semantic-text-primary);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* ── Error state ── */
|
|
103
|
-
.pcoi-radio-group--error .pcoi-radio-group__circle {
|
|
104
|
-
border-color: var(--pcoi-semantic-border-error);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
.pcoi-radio-group__error {
|
|
108
|
-
font-size: var(--pcoi-semantic-type-label-size);
|
|
109
|
-
color: var(--pcoi-semantic-text-error);
|
|
110
|
-
margin: 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* ── Disabled state ── */
|
|
114
|
-
.pcoi-radio-group--disabled {
|
|
115
|
-
opacity: var(--pcoi-effect-opacity-disabled);
|
|
116
|
-
pointer-events: none;
|
|
117
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { OptionItem } from "../types";
|
|
3
|
-
|
|
4
|
-
export interface RadioGroupProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, "onChange"> {
|
|
5
|
-
/** Group label rendered as a fieldset legend */
|
|
6
|
-
label: string;
|
|
7
|
-
/** Shared name for all radio inputs */
|
|
8
|
-
name: string;
|
|
9
|
-
/** List of radio options */
|
|
10
|
-
options: OptionItem[];
|
|
11
|
-
/** Pre-selected value */
|
|
12
|
-
defaultValue?: string;
|
|
13
|
-
/** Error message — when truthy, group enters error state */
|
|
14
|
-
error?: string;
|
|
15
|
-
/** Marks the group as required */
|
|
16
|
-
required?: boolean;
|
|
17
|
-
/** Disables all radio inputs */
|
|
18
|
-
disabled?: boolean;
|
|
19
|
-
/** Called with the selected value when it changes */
|
|
20
|
-
onChange?: (value: string) => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* PCOI RadioGroup — Accessible radio button group using fieldset/legend
|
|
25
|
-
* Tokens: bg/default, border/default, border/error, focus/border, focus/glow,
|
|
26
|
-
* action/primary-bg, text/primary, text/secondary, text/error, radius-full
|
|
27
|
-
*/
|
|
28
|
-
export const RadioGroup = React.forwardRef<HTMLFieldSetElement, RadioGroupProps>(
|
|
29
|
-
(
|
|
30
|
-
{
|
|
31
|
-
label,
|
|
32
|
-
name,
|
|
33
|
-
options,
|
|
34
|
-
defaultValue,
|
|
35
|
-
error,
|
|
36
|
-
required = false,
|
|
37
|
-
disabled = false,
|
|
38
|
-
onChange,
|
|
39
|
-
className = "",
|
|
40
|
-
...rest
|
|
41
|
-
},
|
|
42
|
-
ref
|
|
43
|
-
) => {
|
|
44
|
-
const errorId = error ? `pcoi-field-${name}-error` : undefined;
|
|
45
|
-
|
|
46
|
-
const wrapperClasses = [
|
|
47
|
-
"pcoi-radio-group",
|
|
48
|
-
error ? "pcoi-radio-group--error" : "",
|
|
49
|
-
disabled ? "pcoi-radio-group--disabled" : "",
|
|
50
|
-
className,
|
|
51
|
-
]
|
|
52
|
-
.filter(Boolean)
|
|
53
|
-
.join(" ");
|
|
54
|
-
|
|
55
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
56
|
-
onChange?.(e.target.value);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<fieldset
|
|
61
|
-
ref={ref}
|
|
62
|
-
className={wrapperClasses}
|
|
63
|
-
aria-invalid={!!error}
|
|
64
|
-
aria-describedby={errorId}
|
|
65
|
-
{...rest}
|
|
66
|
-
>
|
|
67
|
-
<legend className="pcoi-radio-group__legend">
|
|
68
|
-
{label}
|
|
69
|
-
{required && (
|
|
70
|
-
<span className="pcoi-radio-group__required" aria-hidden="true">
|
|
71
|
-
*
|
|
72
|
-
</span>
|
|
73
|
-
)}
|
|
74
|
-
</legend>
|
|
75
|
-
<div className="pcoi-radio-group__options">
|
|
76
|
-
{options.map((opt) => {
|
|
77
|
-
const optionId = `pcoi-field-${name}-${opt.value}`;
|
|
78
|
-
return (
|
|
79
|
-
<label
|
|
80
|
-
key={opt.value}
|
|
81
|
-
htmlFor={optionId}
|
|
82
|
-
className="pcoi-radio-group__option"
|
|
83
|
-
>
|
|
84
|
-
<input
|
|
85
|
-
type="radio"
|
|
86
|
-
id={optionId}
|
|
87
|
-
name={name}
|
|
88
|
-
value={opt.value}
|
|
89
|
-
defaultChecked={defaultValue === opt.value}
|
|
90
|
-
required={required}
|
|
91
|
-
disabled={disabled || opt.disabled}
|
|
92
|
-
onChange={handleChange}
|
|
93
|
-
className="pcoi-radio-group__input"
|
|
94
|
-
/>
|
|
95
|
-
<span className="pcoi-radio-group__circle" aria-hidden="true" />
|
|
96
|
-
<span className="pcoi-radio-group__label">{opt.label}</span>
|
|
97
|
-
</label>
|
|
98
|
-
);
|
|
99
|
-
})}
|
|
100
|
-
</div>
|
|
101
|
-
{error && (
|
|
102
|
-
<span id={errorId} className="pcoi-radio-group__error" role="alert">
|
|
103
|
-
{error}
|
|
104
|
-
</span>
|
|
105
|
-
)}
|
|
106
|
-
</fieldset>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
RadioGroup.displayName = "RadioGroup";
|
|
112
|
-
export default RadioGroup;
|
package/src/RadioGroup/index.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/* SectionHeader — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-section-header {
|
|
4
|
-
text-align: center;
|
|
5
|
-
max-width: var(--pcoi-semantic-sizing-container-narrow);
|
|
6
|
-
margin: 0 auto var(--pcoi-semantic-spacing-section-header-margin);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.pcoi-section-header__label {
|
|
10
|
-
display: inline-block;
|
|
11
|
-
font-family: var(--pcoi-semantic-type-mono-font);
|
|
12
|
-
font-size: var(--pcoi-semantic-type-section-label-size);
|
|
13
|
-
font-weight: var(--pcoi-semantic-type-label-weight);
|
|
14
|
-
letter-spacing: var(--pcoi-semantic-type-section-label-letter-spacing);
|
|
15
|
-
text-transform: uppercase;
|
|
16
|
-
color: var(--pcoi-semantic-text-accent);
|
|
17
|
-
margin-bottom: var(--pcoi-semantic-spacing-stack-md);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.pcoi-section-header__title {
|
|
21
|
-
font-size: var(--pcoi-semantic-type-section-title-size);
|
|
22
|
-
font-weight: var(--pcoi-semantic-type-heading-weight);
|
|
23
|
-
line-height: var(--pcoi-semantic-type-heading-line-height);
|
|
24
|
-
letter-spacing: var(--pcoi-semantic-type-heading-letter-spacing);
|
|
25
|
-
color: var(--pcoi-semantic-text-primary);
|
|
26
|
-
margin: 0 0 var(--pcoi-semantic-spacing-stack-md) 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.pcoi-section-header__title em {
|
|
30
|
-
font-style: normal;
|
|
31
|
-
color: var(--pcoi-semantic-text-accent);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.pcoi-section-header__desc {
|
|
35
|
-
font-size: var(--pcoi-semantic-type-heading-sm-size);
|
|
36
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
37
|
-
line-height: var(--pcoi-semantic-type-relaxed-line-height);
|
|
38
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import figma from "@figma/code-connect";
|
|
2
|
-
import { SectionHeader } from "./SectionHeader";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Code Connect: SectionHeader
|
|
6
|
-
* Maps Figma section header properties to React SectionHeader props
|
|
7
|
-
*/
|
|
8
|
-
figma.connect(SectionHeader, "https://www.figma.com/file/PCOIxDesignSystem/PCOI-Design-System?node-id=106-1", {
|
|
9
|
-
props: {
|
|
10
|
-
label: figma.string("Label"),
|
|
11
|
-
title: figma.string("Title"),
|
|
12
|
-
titleEmphasis: figma.string("Title Emphasis"),
|
|
13
|
-
description: figma.string("Description"),
|
|
14
|
-
},
|
|
15
|
-
example: ({ label, title, titleEmphasis, description }) => (
|
|
16
|
-
<SectionHeader
|
|
17
|
-
label={label}
|
|
18
|
-
title={title}
|
|
19
|
-
titleEmphasis={titleEmphasis || undefined}
|
|
20
|
-
description={description || undefined}
|
|
21
|
-
/>
|
|
22
|
-
),
|
|
23
|
-
});
|