@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.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/index.d.ts +829 -609
  4. package/dist/index.js +3517 -2
  5. package/dist/styles.css +2 -0
  6. package/package.json +35 -31
  7. package/dist/components.css +0 -1
  8. package/dist/index.mjs +0 -994
  9. package/src/Badge/Badge.css +0 -40
  10. package/src/Badge/Badge.tsx +0 -36
  11. package/src/Badge/index.ts +0 -2
  12. package/src/Button/Button.css +0 -93
  13. package/src/Button/Button.figma.tsx +0 -27
  14. package/src/Button/Button.test.tsx +0 -32
  15. package/src/Button/Button.tsx +0 -47
  16. package/src/Button/index.ts +0 -1
  17. package/src/Callout/Callout.css +0 -48
  18. package/src/Callout/Callout.figma.tsx +0 -25
  19. package/src/Callout/Callout.tsx +0 -43
  20. package/src/Callout/index.ts +0 -1
  21. package/src/Card/Card.css +0 -88
  22. package/src/Card/Card.figma.tsx +0 -28
  23. package/src/Card/Card.tsx +0 -60
  24. package/src/Card/index.ts +0 -1
  25. package/src/ChatInterface/ChatInterface.css +0 -50
  26. package/src/ChatInterface/ChatInterface.integration.test.tsx +0 -123
  27. package/src/ChatInterface/ChatInterface.tsx +0 -125
  28. package/src/ChatInterface/index.ts +0 -6
  29. package/src/ChatMessage/ChatMessage.css +0 -55
  30. package/src/ChatMessage/ChatMessage.tsx +0 -71
  31. package/src/ChatMessage/index.ts +0 -2
  32. package/src/ChatMessageList/ChatMessageList.css +0 -24
  33. package/src/ChatMessageList/ChatMessageList.test.tsx +0 -70
  34. package/src/ChatMessageList/ChatMessageList.tsx +0 -56
  35. package/src/ChatMessageList/index.ts +0 -2
  36. package/src/Checkbox/Checkbox.css +0 -97
  37. package/src/Checkbox/Checkbox.tsx +0 -70
  38. package/src/Checkbox/index.ts +0 -2
  39. package/src/CitationMark/CitationMark.css +0 -40
  40. package/src/CitationMark/CitationMark.tsx +0 -38
  41. package/src/CitationMark/index.ts +0 -2
  42. package/src/CitedExcerpt/CitedExcerpt.css +0 -75
  43. package/src/CitedExcerpt/CitedExcerpt.tsx +0 -53
  44. package/src/CitedExcerpt/index.ts +0 -2
  45. package/src/ComparisonTable/ComparisonTable.css +0 -66
  46. package/src/ComparisonTable/ComparisonTable.tsx +0 -54
  47. package/src/ComparisonTable/index.ts +0 -1
  48. package/src/ContactForm/ContactForm.css +0 -38
  49. package/src/ContactForm/ContactForm.tsx +0 -58
  50. package/src/ContactForm/index.ts +0 -1
  51. package/src/DataTable/DataTable.css +0 -56
  52. package/src/DataTable/DataTable.tsx +0 -104
  53. package/src/DataTable/index.ts +0 -2
  54. package/src/DocumentOverlay/DocumentOverlay.css +0 -57
  55. package/src/DocumentOverlay/DocumentOverlay.test.tsx +0 -95
  56. package/src/DocumentOverlay/DocumentOverlay.tsx +0 -87
  57. package/src/DocumentOverlay/index.ts +0 -2
  58. package/src/Footer/Footer.css +0 -72
  59. package/src/Footer/Footer.tsx +0 -59
  60. package/src/Footer/index.ts +0 -1
  61. package/src/FormField/FormField.css +0 -78
  62. package/src/FormField/FormField.figma.tsx +0 -28
  63. package/src/FormField/FormField.tsx +0 -103
  64. package/src/FormField/index.ts +0 -2
  65. package/src/HowStep/HowStep.css +0 -48
  66. package/src/HowStep/HowStep.figma.tsx +0 -23
  67. package/src/HowStep/HowStep.tsx +0 -38
  68. package/src/HowStep/index.ts +0 -1
  69. package/src/LogoMark/LogoMark.css +0 -16
  70. package/src/LogoMark/LogoMark.tsx +0 -24
  71. package/src/LogoMark/index.ts +0 -2
  72. package/src/Modal/Modal.css +0 -101
  73. package/src/Modal/Modal.figma.tsx +0 -28
  74. package/src/Modal/Modal.test.tsx +0 -46
  75. package/src/Modal/Modal.tsx +0 -144
  76. package/src/Modal/index.ts +0 -2
  77. package/src/Nav/Nav.css +0 -161
  78. package/src/Nav/Nav.tsx +0 -105
  79. package/src/Nav/index.ts +0 -1
  80. package/src/Panel/Panel.css +0 -35
  81. package/src/Panel/Panel.tsx +0 -61
  82. package/src/Panel/index.ts +0 -2
  83. package/src/PromptBar/PromptBar.css +0 -68
  84. package/src/PromptBar/PromptBar.figma.tsx +0 -25
  85. package/src/PromptBar/PromptBar.test.tsx +0 -83
  86. package/src/PromptBar/PromptBar.tsx +0 -93
  87. package/src/PromptBar/index.ts +0 -2
  88. package/src/RadioGroup/RadioGroup.css +0 -117
  89. package/src/RadioGroup/RadioGroup.tsx +0 -112
  90. package/src/RadioGroup/index.ts +0 -2
  91. package/src/SectionHeader/SectionHeader.css +0 -38
  92. package/src/SectionHeader/SectionHeader.figma.tsx +0 -23
  93. package/src/SectionHeader/SectionHeader.tsx +0 -55
  94. package/src/SectionHeader/index.ts +0 -1
  95. package/src/Select/Select.css +0 -90
  96. package/src/Select/Select.figma.tsx +0 -33
  97. package/src/Select/Select.tsx +0 -100
  98. package/src/Select/index.ts +0 -2
  99. package/src/SignalsPanel/SignalsPanel.css +0 -51
  100. package/src/SignalsPanel/SignalsPanel.tsx +0 -35
  101. package/src/SignalsPanel/index.ts +0 -1
  102. package/src/SuggestionCard/SuggestionCard.css +0 -51
  103. package/src/SuggestionCard/SuggestionCard.tsx +0 -34
  104. package/src/SuggestionCard/index.ts +0 -2
  105. package/src/SuggestionCards/SuggestionCards.css +0 -15
  106. package/src/SuggestionCards/SuggestionCards.test.tsx +0 -27
  107. package/src/SuggestionCards/SuggestionCards.tsx +0 -40
  108. package/src/SuggestionCards/index.ts +0 -2
  109. package/src/Toast/Toast.css +0 -85
  110. package/src/Toast/Toast.tsx +0 -82
  111. package/src/Toast/index.ts +0 -2
  112. package/src/Toggle/Toggle.css +0 -110
  113. package/src/Toggle/Toggle.figma.tsx +0 -24
  114. package/src/Toggle/Toggle.tsx +0 -73
  115. package/src/Toggle/index.ts +0 -2
  116. package/src/TypingIndicator/TypingIndicator.css +0 -70
  117. package/src/TypingIndicator/TypingIndicator.tsx +0 -37
  118. package/src/TypingIndicator/index.ts +0 -2
  119. package/src/index.ts +0 -39
  120. package/src/styles/utilities.css +0 -14
  121. package/src/styles.css +0 -33
  122. 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";
@@ -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
- });
@@ -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;
@@ -1,2 +0,0 @@
1
- export { Select, type SelectProps } from "./Select";
2
- export { default } from "./Select";
@@ -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,2 +0,0 @@
1
- export { SuggestionCard, type SuggestionCardProps } from "./SuggestionCard";
2
- export { default } from "./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;
@@ -1,2 +0,0 @@
1
- export { SuggestionCards, type SuggestionCardsProps } from "./SuggestionCards";
2
- export { default } from "./SuggestionCards";
@@ -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
- }