@pcoi/components 0.1.2 → 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 +829 -609
- package/dist/index.js +3517 -2
- package/dist/styles.css +2 -0
- package/package.json +35 -31
- package/dist/components.css +0 -1
- package/dist/index.mjs +0 -994
- 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 -48
- package/src/Callout/Callout.figma.tsx +0 -25
- package/src/Callout/Callout.tsx +0 -43
- 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 -50
- package/src/ChatInterface/ChatInterface.integration.test.tsx +0 -123
- package/src/ChatInterface/ChatInterface.tsx +0 -125
- 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 -70
- package/src/ChatMessageList/ChatMessageList.tsx +0 -56
- 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 -53
- package/src/CitedExcerpt/index.ts +0 -2
- package/src/ComparisonTable/ComparisonTable.css +0 -66
- package/src/ComparisonTable/ComparisonTable.tsx +0 -54
- package/src/ComparisonTable/index.ts +0 -1
- package/src/ContactForm/ContactForm.css +0 -38
- package/src/ContactForm/ContactForm.tsx +0 -58
- 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 -87
- package/src/DocumentOverlay/index.ts +0 -2
- package/src/Footer/Footer.css +0 -72
- package/src/Footer/Footer.tsx +0 -59
- 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 -144
- package/src/Modal/index.ts +0 -2
- package/src/Nav/Nav.css +0 -161
- package/src/Nav/Nav.tsx +0 -105
- 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 -35
- 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 -82
- 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 -39
- package/src/styles/utilities.css +0 -14
- package/src/styles.css +0 -33
- package/src/types.ts +0 -66
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { HeadingLevel } from "../types";
|
|
3
|
-
|
|
4
|
-
export interface SectionHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
/** Section label (mono, uppercase, accent) e.g. "The Problem" */
|
|
6
|
-
label: string;
|
|
7
|
-
/** Main heading */
|
|
8
|
-
title: string;
|
|
9
|
-
/** Emphasized portion of title (renders in accent color) */
|
|
10
|
-
titleEmphasis?: string;
|
|
11
|
-
/** Optional subtitle/description */
|
|
12
|
-
description?: string;
|
|
13
|
-
/** Heading level for the title (default: "h2") */
|
|
14
|
-
headingLevel?: HeadingLevel;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* PCOI Section Header
|
|
19
|
-
*
|
|
20
|
-
* Centered header pattern used across all major sections.
|
|
21
|
-
* Label + H2 + optional description, max-width 800px.
|
|
22
|
-
*
|
|
23
|
-
* Tokens consumed:
|
|
24
|
-
* - text/accent (label), text/primary (h2), text/secondary (desc)
|
|
25
|
-
* - font-mono (label), font-sans (h2, desc)
|
|
26
|
-
* - spacing-16 (label mb, h2 mb), spacing-64 (section mb)
|
|
27
|
-
* - container-narrow (800px max-width)
|
|
28
|
-
*/
|
|
29
|
-
export const SectionHeader = React.forwardRef<HTMLDivElement, SectionHeaderProps>(
|
|
30
|
-
({ label, title, titleEmphasis, description, headingLevel = "h2", className = "", ...rest }, ref) => {
|
|
31
|
-
const Heading = headingLevel;
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div ref={ref} className={`pcoi-section-header ${className}`} {...rest}>
|
|
35
|
-
<span className="pcoi-section-header__label">{label}</span>
|
|
36
|
-
<Heading className="pcoi-section-header__title">
|
|
37
|
-
{titleEmphasis ? (
|
|
38
|
-
<>
|
|
39
|
-
{title.replace(titleEmphasis, "")}
|
|
40
|
-
<em>{titleEmphasis}</em>
|
|
41
|
-
</>
|
|
42
|
-
) : (
|
|
43
|
-
title
|
|
44
|
-
)}
|
|
45
|
-
</Heading>
|
|
46
|
-
{description && (
|
|
47
|
-
<p className="pcoi-section-header__desc">{description}</p>
|
|
48
|
-
)}
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
SectionHeader.displayName = "SectionHeader";
|
|
55
|
-
export default SectionHeader;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { SectionHeader, type SectionHeaderProps } from "./SectionHeader";
|
package/src/Select/Select.css
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/* Select — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-select {
|
|
4
|
-
display: flex;
|
|
5
|
-
flex-direction: column;
|
|
6
|
-
gap: var(--pcoi-semantic-spacing-form-gap-compact);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.pcoi-select__label {
|
|
10
|
-
font-size: var(--pcoi-semantic-type-label-size);
|
|
11
|
-
font-weight: var(--pcoi-semantic-type-label-weight);
|
|
12
|
-
letter-spacing: var(--pcoi-semantic-type-label-letter-spacing);
|
|
13
|
-
text-transform: uppercase;
|
|
14
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.pcoi-select__required {
|
|
18
|
-
color: var(--pcoi-semantic-text-error);
|
|
19
|
-
margin-left: var(--pcoi-semantic-spacing-inline-2xs);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.pcoi-select__wrapper {
|
|
23
|
-
position: relative;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.pcoi-select__native {
|
|
27
|
-
appearance: none;
|
|
28
|
-
width: 100%;
|
|
29
|
-
font-family: var(--pcoi-semantic-type-body-font);
|
|
30
|
-
font-size: var(--pcoi-semantic-type-body-size);
|
|
31
|
-
color: var(--pcoi-semantic-text-primary);
|
|
32
|
-
background: var(--pcoi-semantic-bg-default);
|
|
33
|
-
border: 1px solid var(--pcoi-semantic-border-default);
|
|
34
|
-
border-radius: var(--pcoi-semantic-radius-input);
|
|
35
|
-
padding: var(--pcoi-semantic-spacing-input-y) var(--pcoi-semantic-spacing-btn-x-lg) var(--pcoi-semantic-spacing-input-y) var(--pcoi-semantic-spacing-input-x-compact);
|
|
36
|
-
cursor: pointer;
|
|
37
|
-
transition: border-color var(--pcoi-effect-transition-fast, 0.2s ease),
|
|
38
|
-
box-shadow var(--pcoi-effect-transition-fast, 0.2s ease);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.pcoi-select__native:hover {
|
|
42
|
-
border-color: var(--pcoi-semantic-border-input-hover);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.pcoi-select__native:active {
|
|
46
|
-
transform: var(--pcoi-effect-transform-press-scale-input);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.pcoi-select__native:focus {
|
|
50
|
-
outline: none;
|
|
51
|
-
border-color: var(--pcoi-semantic-focus-border);
|
|
52
|
-
box-shadow: var(--pcoi-effect-shadow-focus-ring);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.pcoi-select__native option {
|
|
56
|
-
background: var(--pcoi-semantic-bg-default);
|
|
57
|
-
color: var(--pcoi-semantic-text-primary);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.pcoi-select__chevron {
|
|
61
|
-
position: absolute;
|
|
62
|
-
right: var(--pcoi-semantic-spacing-input-x-compact);
|
|
63
|
-
top: 50%;
|
|
64
|
-
transform: translateY(-50%);
|
|
65
|
-
pointer-events: none;
|
|
66
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
67
|
-
display: flex;
|
|
68
|
-
align-items: center;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/* ── Error state ── */
|
|
72
|
-
.pcoi-select--error .pcoi-select__native {
|
|
73
|
-
border-color: var(--pcoi-semantic-border-error);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.pcoi-select--error .pcoi-select__native:focus {
|
|
77
|
-
box-shadow: var(--pcoi-effect-shadow-focus-ring-error);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.pcoi-select__error {
|
|
81
|
-
font-size: var(--pcoi-semantic-type-label-size);
|
|
82
|
-
color: var(--pcoi-semantic-text-error);
|
|
83
|
-
margin: 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* ── Disabled state ── */
|
|
87
|
-
.pcoi-select--disabled {
|
|
88
|
-
opacity: var(--pcoi-effect-opacity-disabled);
|
|
89
|
-
pointer-events: none;
|
|
90
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import figma from "@figma/code-connect";
|
|
2
|
-
import { Select } from "./Select";
|
|
3
|
-
|
|
4
|
-
const options = [
|
|
5
|
-
{ value: "one", label: "Option One" },
|
|
6
|
-
{ value: "two", label: "Option Two" },
|
|
7
|
-
{ value: "three", label: "Option Three" },
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Code Connect: Select
|
|
12
|
-
* Maps Figma select field states to React Select props
|
|
13
|
-
*/
|
|
14
|
-
figma.connect(Select, "https://www.figma.com/file/PCOIxDesignSystem/PCOI-Design-System?node-id=109-1", {
|
|
15
|
-
props: {
|
|
16
|
-
label: figma.string("Label"),
|
|
17
|
-
placeholder: figma.string("Placeholder"),
|
|
18
|
-
required: figma.boolean("Required"),
|
|
19
|
-
disabled: figma.boolean("Disabled"),
|
|
20
|
-
hasError: figma.boolean("Error"),
|
|
21
|
-
},
|
|
22
|
-
example: ({ label, placeholder, required, disabled, hasError }) => (
|
|
23
|
-
<Select
|
|
24
|
-
name="mapped-select"
|
|
25
|
-
label={label}
|
|
26
|
-
options={options}
|
|
27
|
-
placeholder={placeholder || undefined}
|
|
28
|
-
required={required}
|
|
29
|
-
disabled={disabled}
|
|
30
|
-
error={hasError ? "Please select an option." : undefined}
|
|
31
|
-
/>
|
|
32
|
-
),
|
|
33
|
-
});
|
package/src/Select/Select.tsx
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { ChevronDownIcon } from "@pcoi/icons";
|
|
3
|
-
import type { OptionItem } from "../types";
|
|
4
|
-
|
|
5
|
-
export interface SelectProps
|
|
6
|
-
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "size"> {
|
|
7
|
-
/** Visible label text */
|
|
8
|
-
label: string;
|
|
9
|
-
/** Unique field name, also used to generate id */
|
|
10
|
-
name: string;
|
|
11
|
-
/** List of options */
|
|
12
|
-
options: OptionItem[];
|
|
13
|
-
/** Placeholder text shown as first disabled option */
|
|
14
|
-
placeholder?: string;
|
|
15
|
-
/** Error message — when truthy, field enters error state */
|
|
16
|
-
error?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* PCOI Select — Native dropdown with custom styling
|
|
21
|
-
* Tokens: bg/default, border/default, border/error, focus/border, focus/glow,
|
|
22
|
-
* text/primary, text/secondary, text/muted, text/error, radius-sm
|
|
23
|
-
*/
|
|
24
|
-
export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
25
|
-
(
|
|
26
|
-
{
|
|
27
|
-
label,
|
|
28
|
-
name,
|
|
29
|
-
options,
|
|
30
|
-
placeholder,
|
|
31
|
-
error,
|
|
32
|
-
required = false,
|
|
33
|
-
disabled = false,
|
|
34
|
-
className = "",
|
|
35
|
-
...props
|
|
36
|
-
},
|
|
37
|
-
ref
|
|
38
|
-
) => {
|
|
39
|
-
const fieldId = `pcoi-field-${name}`;
|
|
40
|
-
const errorId = error ? `${fieldId}-error` : undefined;
|
|
41
|
-
|
|
42
|
-
const wrapperClasses = [
|
|
43
|
-
"pcoi-select",
|
|
44
|
-
error ? "pcoi-select--error" : "",
|
|
45
|
-
disabled ? "pcoi-select--disabled" : "",
|
|
46
|
-
className,
|
|
47
|
-
]
|
|
48
|
-
.filter(Boolean)
|
|
49
|
-
.join(" ");
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<div className={wrapperClasses}>
|
|
53
|
-
<label htmlFor={fieldId} className="pcoi-select__label">
|
|
54
|
-
{label}
|
|
55
|
-
{required && (
|
|
56
|
-
<span className="pcoi-select__required" aria-hidden="true">
|
|
57
|
-
*
|
|
58
|
-
</span>
|
|
59
|
-
)}
|
|
60
|
-
</label>
|
|
61
|
-
<div className="pcoi-select__wrapper">
|
|
62
|
-
<select
|
|
63
|
-
ref={ref}
|
|
64
|
-
id={fieldId}
|
|
65
|
-
name={name}
|
|
66
|
-
required={required}
|
|
67
|
-
disabled={disabled}
|
|
68
|
-
aria-invalid={!!error}
|
|
69
|
-
aria-describedby={errorId}
|
|
70
|
-
className="pcoi-select__native"
|
|
71
|
-
defaultValue={placeholder ? "" : undefined}
|
|
72
|
-
{...props}
|
|
73
|
-
>
|
|
74
|
-
{placeholder && (
|
|
75
|
-
<option value="" disabled hidden>
|
|
76
|
-
{placeholder}
|
|
77
|
-
</option>
|
|
78
|
-
)}
|
|
79
|
-
{options.map((opt) => (
|
|
80
|
-
<option key={opt.value} value={opt.value} disabled={opt.disabled}>
|
|
81
|
-
{opt.label}
|
|
82
|
-
</option>
|
|
83
|
-
))}
|
|
84
|
-
</select>
|
|
85
|
-
<span className="pcoi-select__chevron" aria-hidden="true">
|
|
86
|
-
<ChevronDownIcon size={16} />
|
|
87
|
-
</span>
|
|
88
|
-
</div>
|
|
89
|
-
{error && (
|
|
90
|
-
<span id={errorId} className="pcoi-select__error" role="alert">
|
|
91
|
-
{error}
|
|
92
|
-
</span>
|
|
93
|
-
)}
|
|
94
|
-
</div>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
Select.displayName = "Select";
|
|
100
|
-
export default Select;
|
package/src/Select/index.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/* SignalsPanel — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-signals {
|
|
4
|
-
background: var(--pcoi-semantic-bg-surface);
|
|
5
|
-
border: 1px solid var(--pcoi-semantic-border-default);
|
|
6
|
-
border-radius: var(--pcoi-semantic-radius-card);
|
|
7
|
-
padding: var(--pcoi-semantic-spacing-component-gap) var(--pcoi-semantic-spacing-inline-xl);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.pcoi-signals__title {
|
|
11
|
-
font-size: var(--pcoi-semantic-type-heading-sm-size);
|
|
12
|
-
font-weight: var(--pcoi-semantic-type-emphasis-weight);
|
|
13
|
-
color: var(--pcoi-semantic-text-accent);
|
|
14
|
-
margin: 0 0 var(--pcoi-semantic-spacing-panel-padding) 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.pcoi-signals__list {
|
|
18
|
-
list-style: none;
|
|
19
|
-
padding: 0;
|
|
20
|
-
margin: 0;
|
|
21
|
-
display: flex;
|
|
22
|
-
flex-direction: column;
|
|
23
|
-
gap: var(--pcoi-semantic-spacing-stack-compact);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.pcoi-signals__item {
|
|
27
|
-
position: relative;
|
|
28
|
-
padding-left: var(--pcoi-semantic-spacing-panel-padding);
|
|
29
|
-
font-size: var(--pcoi-semantic-type-body-compact-size);
|
|
30
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
31
|
-
line-height: var(--pcoi-semantic-type-body-compact-line-height);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.pcoi-signals__item::before {
|
|
35
|
-
content: "";
|
|
36
|
-
position: absolute;
|
|
37
|
-
left: 0;
|
|
38
|
-
top: 8px;
|
|
39
|
-
width: var(--pcoi-semantic-sizing-bullet-size);
|
|
40
|
-
height: var(--pcoi-semantic-sizing-bullet-size);
|
|
41
|
-
border-radius: var(--pcoi-semantic-radius-badge);
|
|
42
|
-
background: var(--pcoi-semantic-text-accent);
|
|
43
|
-
opacity: 0.5;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/* ── Responsive ── */
|
|
47
|
-
@media (max-width: 768px) {
|
|
48
|
-
.pcoi-signals {
|
|
49
|
-
padding: var(--pcoi-semantic-spacing-panel-padding) var(--pcoi-semantic-spacing-inline-lg);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { HeadingLevel } from "../types";
|
|
3
|
-
|
|
4
|
-
export interface SignalsPanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
/** Panel heading text */
|
|
6
|
-
title?: string;
|
|
7
|
-
/** List of signal statements displayed as bullet points */
|
|
8
|
-
signals: string[];
|
|
9
|
-
/** Heading level for the title (default: "h3") */
|
|
10
|
-
headingLevel?: HeadingLevel;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* PCOI Signals Panel — "You'll know this is for you if:" section
|
|
15
|
-
* Tokens: bg/surface, border/default, radius-md, spacing-40/48, text/accent
|
|
16
|
-
*/
|
|
17
|
-
export const SignalsPanel = React.forwardRef<HTMLDivElement, SignalsPanelProps>(
|
|
18
|
-
({ title = "You'll know this is for you if:", signals, headingLevel = "h3", className = "", ...rest }, ref) => {
|
|
19
|
-
const Heading = headingLevel;
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<div ref={ref} className={`pcoi-signals ${className}`} {...rest}>
|
|
23
|
-
<Heading className="pcoi-signals__title">{title}</Heading>
|
|
24
|
-
<ul className="pcoi-signals__list">
|
|
25
|
-
{signals.map((signal, i) => (
|
|
26
|
-
<li key={i} className="pcoi-signals__item">{signal}</li>
|
|
27
|
-
))}
|
|
28
|
-
</ul>
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
SignalsPanel.displayName = "SignalsPanel";
|
|
35
|
-
export default SignalsPanel;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { SignalsPanel, type SignalsPanelProps } from "./SignalsPanel";
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/* SuggestionCard — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-suggestion-card {
|
|
4
|
-
display: flex;
|
|
5
|
-
align-items: center;
|
|
6
|
-
gap: var(--pcoi-semantic-spacing-panel-gap);
|
|
7
|
-
width: 100%;
|
|
8
|
-
padding: var(--pcoi-semantic-spacing-panel-padding-sm) var(--pcoi-semantic-spacing-card-gap);
|
|
9
|
-
font-family: var(--pcoi-semantic-type-body-font);
|
|
10
|
-
font-size: var(--pcoi-semantic-type-body-size);
|
|
11
|
-
line-height: var(--pcoi-semantic-type-body-line-height);
|
|
12
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
13
|
-
text-align: left;
|
|
14
|
-
background: var(--pcoi-semantic-bg-card);
|
|
15
|
-
border: 1px solid var(--pcoi-semantic-border-card);
|
|
16
|
-
border-radius: var(--pcoi-semantic-radius-card);
|
|
17
|
-
cursor: pointer;
|
|
18
|
-
transition:
|
|
19
|
-
background var(--pcoi-effect-transition-medium),
|
|
20
|
-
border-color var(--pcoi-effect-transition-medium),
|
|
21
|
-
transform var(--pcoi-effect-transition-medium),
|
|
22
|
-
box-shadow var(--pcoi-effect-transition-medium);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.pcoi-suggestion-card:hover {
|
|
26
|
-
background: var(--pcoi-semantic-bg-card-hover);
|
|
27
|
-
border-color: var(--pcoi-semantic-border-card-hover);
|
|
28
|
-
transform: var(--pcoi-effect-transform-hover-lift-sm);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.pcoi-suggestion-card:active {
|
|
32
|
-
transform: var(--pcoi-effect-transform-press-scale);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.pcoi-suggestion-card:focus-visible {
|
|
36
|
-
outline: none;
|
|
37
|
-
box-shadow: var(--pcoi-effect-shadow-focus-ring);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.pcoi-suggestion-card__icon {
|
|
41
|
-
display: flex;
|
|
42
|
-
align-items: center;
|
|
43
|
-
justify-content: center;
|
|
44
|
-
flex-shrink: 0;
|
|
45
|
-
color: var(--pcoi-semantic-text-accent);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.pcoi-suggestion-card__label {
|
|
49
|
-
flex: 1;
|
|
50
|
-
min-width: 0;
|
|
51
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
export interface SuggestionCardProps
|
|
4
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
-
/** Display text */
|
|
6
|
-
label: string;
|
|
7
|
-
/** Optional icon rendered before the label */
|
|
8
|
-
icon?: React.ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* PCOI SuggestionCard — Clickable FAQ card for chat empty state
|
|
13
|
-
* Tokens: bg/card, bg/card-hover, border/card, border/card-hover,
|
|
14
|
-
* text/secondary, text/accent, radius-md, transition-medium,
|
|
15
|
-
* transform/hover-lift-sm, transform/press-scale, shadow/focus-ring
|
|
16
|
-
*/
|
|
17
|
-
export const SuggestionCard = React.forwardRef<
|
|
18
|
-
HTMLButtonElement,
|
|
19
|
-
SuggestionCardProps
|
|
20
|
-
>(({ label, icon, className = "", ...rest }, ref) => {
|
|
21
|
-
const classes = ["pcoi-suggestion-card", className]
|
|
22
|
-
.filter(Boolean)
|
|
23
|
-
.join(" ");
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<button ref={ref} type="button" className={classes} {...rest}>
|
|
27
|
-
{icon && <span className="pcoi-suggestion-card__icon">{icon}</span>}
|
|
28
|
-
<span className="pcoi-suggestion-card__label">{label}</span>
|
|
29
|
-
</button>
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
SuggestionCard.displayName = "SuggestionCard";
|
|
34
|
-
export default SuggestionCard;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/* SuggestionCards — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-suggestion-cards {
|
|
4
|
-
display: flex;
|
|
5
|
-
flex-direction: column;
|
|
6
|
-
gap: var(--pcoi-semantic-spacing-stack-md);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/* ── Responsive: 3-col grid on tablet and up ── */
|
|
10
|
-
@media (min-width: 768px) {
|
|
11
|
-
.pcoi-suggestion-cards {
|
|
12
|
-
display: grid;
|
|
13
|
-
grid-template-columns: repeat(3, 1fr);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import type { Suggestion } from "../types";
|
|
4
|
-
import { SuggestionCards } from "./SuggestionCards";
|
|
5
|
-
|
|
6
|
-
describe("SuggestionCards", () => {
|
|
7
|
-
it("renders suggestions and calls onSelect with clicked item", () => {
|
|
8
|
-
const suggestions: Suggestion[] = [
|
|
9
|
-
{ id: "s1", label: "What is PCOI?" },
|
|
10
|
-
{ id: "s2", label: "How does indexing work?" },
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
const onSelect = vi.fn();
|
|
14
|
-
|
|
15
|
-
render(<SuggestionCards suggestions={suggestions} onSelect={onSelect} />);
|
|
16
|
-
|
|
17
|
-
const first = screen.getByRole("button", { name: "What is PCOI?" });
|
|
18
|
-
const second = screen.getByRole("button", { name: "How does indexing work?" });
|
|
19
|
-
|
|
20
|
-
expect(first).toBeInTheDocument();
|
|
21
|
-
expect(second).toBeInTheDocument();
|
|
22
|
-
|
|
23
|
-
fireEvent.click(second);
|
|
24
|
-
expect(onSelect).toHaveBeenCalledTimes(1);
|
|
25
|
-
expect(onSelect).toHaveBeenCalledWith(suggestions[1]);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { SuggestionCard } from "../SuggestionCard/SuggestionCard";
|
|
3
|
-
import type { Suggestion } from "../types";
|
|
4
|
-
|
|
5
|
-
export interface SuggestionCardsProps
|
|
6
|
-
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect"> {
|
|
7
|
-
/** Array of suggestion prompts */
|
|
8
|
-
suggestions: Suggestion[];
|
|
9
|
-
/** Called when a suggestion card is clicked */
|
|
10
|
-
onSelect: (suggestion: Suggestion) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* PCOI SuggestionCards — 3-column grid of suggestion cards for chat empty state
|
|
15
|
-
* Tokens: spacing-16 (gap), grid/suggestions
|
|
16
|
-
*/
|
|
17
|
-
export const SuggestionCards = React.forwardRef<
|
|
18
|
-
HTMLDivElement,
|
|
19
|
-
SuggestionCardsProps
|
|
20
|
-
>(({ suggestions, onSelect, className = "", ...rest }, ref) => {
|
|
21
|
-
const classes = ["pcoi-suggestion-cards", className]
|
|
22
|
-
.filter(Boolean)
|
|
23
|
-
.join(" ");
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div ref={ref} className={classes} {...rest}>
|
|
27
|
-
{suggestions.map((s) => (
|
|
28
|
-
<SuggestionCard
|
|
29
|
-
key={s.id}
|
|
30
|
-
label={s.label}
|
|
31
|
-
icon={s.icon}
|
|
32
|
-
onClick={() => onSelect(s)}
|
|
33
|
-
/>
|
|
34
|
-
))}
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
SuggestionCards.displayName = "SuggestionCards";
|
|
40
|
-
export default SuggestionCards;
|
package/src/Toast/Toast.css
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/* Toast — @pcoi/components */
|
|
2
|
-
|
|
3
|
-
.pcoi-toast {
|
|
4
|
-
position: fixed;
|
|
5
|
-
bottom: var(--pcoi-semantic-spacing-btn-x);
|
|
6
|
-
right: var(--pcoi-semantic-spacing-btn-x);
|
|
7
|
-
z-index: var(--pcoi-semantic-layout-z-toast, 600);
|
|
8
|
-
animation: pcoi-toast-slide-in 0.3s ease;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.pcoi-toast__content {
|
|
12
|
-
display: flex;
|
|
13
|
-
align-items: center;
|
|
14
|
-
gap: var(--pcoi-semantic-spacing-panel-gap);
|
|
15
|
-
font-family: var(--pcoi-semantic-type-body-font);
|
|
16
|
-
background: var(--pcoi-semantic-surface-elevated);
|
|
17
|
-
border: 1px solid var(--pcoi-semantic-border-default);
|
|
18
|
-
border-radius: var(--pcoi-semantic-radius-card);
|
|
19
|
-
box-shadow: var(--pcoi-effect-shadow-elevated);
|
|
20
|
-
padding: var(--pcoi-semantic-spacing-btn-y-comfortable) var(--pcoi-semantic-spacing-card-gap);
|
|
21
|
-
min-width: var(--pcoi-semantic-sizing-toast-width-min);
|
|
22
|
-
max-width: var(--pcoi-semantic-sizing-toast-width-max);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/* ── Left accent border per variant ── */
|
|
26
|
-
.pcoi-toast--success .pcoi-toast__content {
|
|
27
|
-
border-left: var(--pcoi-semantic-sizing-accent-border-width) solid var(--pcoi-semantic-border-success);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.pcoi-toast--error .pcoi-toast__content {
|
|
31
|
-
border-left: var(--pcoi-semantic-sizing-accent-border-width) solid var(--pcoi-semantic-border-error);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.pcoi-toast--warning .pcoi-toast__content {
|
|
35
|
-
border-left: var(--pcoi-semantic-sizing-accent-border-width) solid var(--pcoi-semantic-border-warning);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.pcoi-toast--info .pcoi-toast__content {
|
|
39
|
-
border-left: var(--pcoi-semantic-sizing-accent-border-width) solid var(--pcoi-semantic-border-info);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.pcoi-toast__message {
|
|
43
|
-
flex: 1;
|
|
44
|
-
font-size: var(--pcoi-semantic-type-body-size);
|
|
45
|
-
color: var(--pcoi-semantic-text-primary);
|
|
46
|
-
line-height: var(--pcoi-semantic-type-body-compact-line-height);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.pcoi-toast__close {
|
|
50
|
-
background: none;
|
|
51
|
-
border: none;
|
|
52
|
-
color: var(--pcoi-semantic-text-secondary);
|
|
53
|
-
font-size: var(--pcoi-semantic-type-close-sm-size);
|
|
54
|
-
line-height: var(--pcoi-semantic-type-none-line-height);
|
|
55
|
-
cursor: pointer;
|
|
56
|
-
padding: var(--pcoi-semantic-spacing-control-padding-2xs);
|
|
57
|
-
border-radius: var(--pcoi-semantic-radius-btn);
|
|
58
|
-
flex-shrink: 0;
|
|
59
|
-
transition: color var(--pcoi-effect-transition-fast, 0.2s ease);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.pcoi-toast__close:hover {
|
|
63
|
-
color: var(--pcoi-semantic-text-primary);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.pcoi-toast__close:active {
|
|
67
|
-
transform: var(--pcoi-effect-transform-press-scale-icon);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.pcoi-toast__close:focus-visible {
|
|
71
|
-
outline: none;
|
|
72
|
-
box-shadow: var(--pcoi-effect-shadow-focus-ring);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* ── Animation ── */
|
|
76
|
-
@keyframes pcoi-toast-slide-in {
|
|
77
|
-
from {
|
|
78
|
-
transform: translateX(100%);
|
|
79
|
-
opacity: 0;
|
|
80
|
-
}
|
|
81
|
-
to {
|
|
82
|
-
transform: translateX(0);
|
|
83
|
-
opacity: 1;
|
|
84
|
-
}
|
|
85
|
-
}
|