@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.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/index.d.ts +826 -568
  4. package/dist/index.js +3517 -2
  5. package/dist/styles.css +2 -0
  6. package/package.json +36 -32
  7. package/dist/components.css +0 -1
  8. package/dist/index.mjs +0 -969
  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 -43
  18. package/src/Callout/Callout.figma.tsx +0 -25
  19. package/src/Callout/Callout.tsx +0 -39
  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 -49
  26. package/src/ChatInterface/ChatInterface.integration.test.tsx +0 -123
  27. package/src/ChatInterface/ChatInterface.tsx +0 -120
  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 -74
  34. package/src/ChatMessageList/ChatMessageList.tsx +0 -51
  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 -51
  44. package/src/CitedExcerpt/index.ts +0 -2
  45. package/src/ComparisonTable/ComparisonTable.css +0 -66
  46. package/src/ComparisonTable/ComparisonTable.tsx +0 -48
  47. package/src/ComparisonTable/index.ts +0 -1
  48. package/src/ContactForm/ContactForm.css +0 -38
  49. package/src/ContactForm/ContactForm.tsx +0 -57
  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 -86
  57. package/src/DocumentOverlay/index.ts +0 -2
  58. package/src/Footer/Footer.css +0 -72
  59. package/src/Footer/Footer.tsx +0 -56
  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 -139
  76. package/src/Modal/index.ts +0 -2
  77. package/src/Nav/Nav.css +0 -161
  78. package/src/Nav/Nav.tsx +0 -101
  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 -33
  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 -77
  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 -37
  120. package/src/styles/utilities.css +0 -14
  121. package/src/styles.css +0 -32
  122. package/src/types.ts +0 -65
@@ -1,70 +0,0 @@
1
- import React from "react";
2
-
3
- export interface CheckboxProps
4
- extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
5
- /** Visible label text */
6
- label: string;
7
- /** Unique field name, also used to generate id */
8
- name: string;
9
- /** Error message — when truthy, field enters error state */
10
- error?: string;
11
- }
12
-
13
- /**
14
- * PCOI Checkbox — Custom styled checkbox with label
15
- * Tokens: bg/default, border/default, border/error, focus/border, focus/glow,
16
- * action/primary-bg, action/primary-text, text/primary, text/error, radius-sm
17
- */
18
- export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
19
- (
20
- {
21
- label,
22
- name,
23
- error,
24
- disabled = false,
25
- className = "",
26
- ...props
27
- },
28
- ref
29
- ) => {
30
- const fieldId = `pcoi-field-${name}`;
31
- const errorId = error ? `${fieldId}-error` : undefined;
32
-
33
- const wrapperClasses = [
34
- "pcoi-checkbox",
35
- error ? "pcoi-checkbox--error" : "",
36
- disabled ? "pcoi-checkbox--disabled" : "",
37
- className,
38
- ]
39
- .filter(Boolean)
40
- .join(" ");
41
-
42
- return (
43
- <div className={wrapperClasses}>
44
- <label htmlFor={fieldId} className="pcoi-checkbox__control">
45
- <input
46
- ref={ref}
47
- type="checkbox"
48
- id={fieldId}
49
- name={name}
50
- disabled={disabled}
51
- aria-invalid={!!error}
52
- aria-describedby={errorId}
53
- className="pcoi-checkbox__input"
54
- {...props}
55
- />
56
- <span className="pcoi-checkbox__box" aria-hidden="true" />
57
- <span className="pcoi-checkbox__label">{label}</span>
58
- </label>
59
- {error && (
60
- <span id={errorId} className="pcoi-checkbox__error" role="alert">
61
- {error}
62
- </span>
63
- )}
64
- </div>
65
- );
66
- }
67
- );
68
-
69
- Checkbox.displayName = "Checkbox";
70
- export default Checkbox;
@@ -1,2 +0,0 @@
1
- export { Checkbox, type CheckboxProps } from "./Checkbox";
2
- export { default } from "./Checkbox";
@@ -1,40 +0,0 @@
1
- /* CitationMark — @pcoi/components */
2
-
3
- .pcoi-citation-mark {
4
- display: inline-flex;
5
- align-items: center;
6
- justify-content: center;
7
- min-width: 32px;
8
- min-height: 32px;
9
- padding: var(--pcoi-semantic-spacing-inline-2xs) var(--pcoi-semantic-spacing-chip-x);
10
- font-family: var(--pcoi-semantic-type-mono-font);
11
- font-size: var(--pcoi-semantic-type-body-compact-size);
12
- font-weight: var(--pcoi-semantic-type-label-weight);
13
- line-height: var(--pcoi-semantic-type-none-line-height);
14
- color: var(--pcoi-semantic-text-accent);
15
- background: var(--pcoi-semantic-surface-accent-dim);
16
- border: 1px solid var(--pcoi-semantic-border-accent-dim);
17
- border-radius: var(--pcoi-semantic-radius-badge);
18
- cursor: pointer;
19
- vertical-align: middle;
20
- transition:
21
- background var(--pcoi-effect-transition-fast),
22
- color var(--pcoi-effect-transition-fast),
23
- border-color var(--pcoi-effect-transition-fast),
24
- transform var(--pcoi-effect-transition-fast);
25
- }
26
-
27
- .pcoi-citation-mark:hover {
28
- color: var(--pcoi-semantic-text-accent-hover);
29
- background: var(--pcoi-semantic-surface-accent-dim);
30
- border-color: var(--pcoi-semantic-border-accent-subtle);
31
- }
32
-
33
- .pcoi-citation-mark:active {
34
- transform: var(--pcoi-effect-transform-press-scale-control);
35
- }
36
-
37
- .pcoi-citation-mark:focus-visible {
38
- outline: none;
39
- box-shadow: var(--pcoi-effect-shadow-focus-ring);
40
- }
@@ -1,38 +0,0 @@
1
- import React from "react";
2
-
3
- export interface CitationMarkProps
4
- extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
- /** 1-based citation index displayed in the button */
6
- index: number;
7
- /** Tooltip text (defaults to "Citation {index}") */
8
- sourceLabel?: string;
9
- }
10
-
11
- /**
12
- * PCOI CitationMark — Inline pill-shaped citation button [1]
13
- * Tokens: text/accent, surface/accent-dim, border/accent-dim,
14
- * radius-full, font-family/mono, transition-fast,
15
- * shadow/focus-ring, transform/press-scale-control
16
- */
17
- export const CitationMark = React.forwardRef<
18
- HTMLButtonElement,
19
- CitationMarkProps
20
- >(({ index, sourceLabel, className = "", ...rest }, ref) => {
21
- const classes = ["pcoi-citation-mark", className].filter(Boolean).join(" ");
22
-
23
- return (
24
- <button
25
- ref={ref}
26
- type="button"
27
- className={classes}
28
- title={sourceLabel ?? `Citation ${index}`}
29
- aria-label={sourceLabel ?? `Citation ${index}`}
30
- {...rest}
31
- >
32
- {index}
33
- </button>
34
- );
35
- });
36
-
37
- CitationMark.displayName = "CitationMark";
38
- export default CitationMark;
@@ -1,2 +0,0 @@
1
- export { CitationMark, type CitationMarkProps } from "./CitationMark";
2
- export { default } from "./CitationMark";
@@ -1,75 +0,0 @@
1
- /* CitedExcerpt — @pcoi/components */
2
-
3
- .pcoi-cited-excerpt {
4
- display: flex;
5
- flex-direction: column;
6
- gap: var(--pcoi-semantic-spacing-form-gap-compact);
7
- padding: var(--pcoi-semantic-spacing-input-y);
8
- background: var(--pcoi-semantic-surface-accent-dim);
9
- border-left: var(--pcoi-semantic-sizing-accent-border-width) solid
10
- var(--pcoi-semantic-border-accent-dim);
11
- border-radius: var(--pcoi-semantic-radius-input);
12
- }
13
-
14
- .pcoi-cited-excerpt__source {
15
- display: inline-flex;
16
- align-items: center;
17
- align-self: flex-start;
18
- padding: 0;
19
- font-family: var(--pcoi-semantic-type-body-font);
20
- font-size: var(--pcoi-semantic-type-body-sm-size);
21
- color: var(--pcoi-semantic-text-accent);
22
- background: none;
23
- border: none;
24
- cursor: pointer;
25
- transition:
26
- color var(--pcoi-effect-transition-fast);
27
- }
28
-
29
- .pcoi-cited-excerpt__source:hover {
30
- color: var(--pcoi-semantic-text-accent-hover);
31
- text-decoration: underline;
32
- }
33
-
34
- .pcoi-cited-excerpt__source:focus-visible {
35
- outline: none;
36
- box-shadow: var(--pcoi-effect-shadow-focus-ring);
37
- border-radius: var(--pcoi-semantic-radius-btn);
38
- }
39
-
40
- .pcoi-cited-excerpt__body {
41
- display: flex;
42
- align-items: baseline;
43
- gap: var(--pcoi-semantic-spacing-form-gap-compact);
44
- }
45
-
46
- .pcoi-cited-excerpt__index {
47
- flex-shrink: 0;
48
- padding: 0;
49
- background: none;
50
- border: none;
51
- cursor: pointer;
52
- font-family: var(--pcoi-semantic-type-mono-font);
53
- font-size: var(--pcoi-semantic-type-body-compact-size);
54
- font-weight: var(--pcoi-semantic-type-label-weight);
55
- color: var(--pcoi-semantic-text-accent);
56
- transition: color var(--pcoi-effect-transition-fast);
57
- }
58
-
59
- .pcoi-cited-excerpt__index:hover {
60
- color: var(--pcoi-semantic-text-accent-hover);
61
- }
62
-
63
- .pcoi-cited-excerpt__index:focus-visible {
64
- outline: none;
65
- box-shadow: var(--pcoi-effect-shadow-focus-ring);
66
- border-radius: var(--pcoi-semantic-radius-btn);
67
- }
68
-
69
- .pcoi-cited-excerpt__text {
70
- margin: 0;
71
- font-family: var(--pcoi-semantic-type-body-font);
72
- font-size: var(--pcoi-semantic-type-body-compact-size);
73
- color: var(--pcoi-semantic-text-secondary);
74
- line-height: var(--pcoi-semantic-type-body-line-height);
75
- }
@@ -1,51 +0,0 @@
1
- import React from "react";
2
-
3
- export interface CitedExcerptProps
4
- extends React.HTMLAttributes<HTMLDivElement> {
5
- /** 1-based citation index */
6
- index: number;
7
- /** Short excerpt from the source document */
8
- excerpt: string;
9
- /** Source document title */
10
- sourceTitle: string;
11
- /** Called when the source link is clicked */
12
- onSourceClick?: () => void;
13
- }
14
-
15
- /**
16
- * PCOI CitedExcerpt — Blockquote with excerpt text above, clickable index + source below
17
- * Tokens: surface/accent-dim, border/accent-dim, accent-border-w,
18
- * radius-sm, text/secondary, text/accent, type/body-compact-size
19
- */
20
- export const CitedExcerpt = React.forwardRef<HTMLDivElement, CitedExcerptProps>(
21
- ({ index, excerpt, sourceTitle, onSourceClick, className = "", ...rest }, ref) => {
22
- const classes = ["pcoi-cited-excerpt", className]
23
- .filter(Boolean)
24
- .join(" ");
25
-
26
- return (
27
- <div ref={ref} className={classes} {...rest}>
28
- <button
29
- type="button"
30
- className="pcoi-cited-excerpt__source"
31
- onClick={onSourceClick}
32
- >
33
- {sourceTitle}
34
- </button>
35
- <div className="pcoi-cited-excerpt__body">
36
- <button
37
- type="button"
38
- className="pcoi-cited-excerpt__index"
39
- onClick={onSourceClick}
40
- >
41
- [{index}]
42
- </button>
43
- <p className="pcoi-cited-excerpt__text">{excerpt}</p>
44
- </div>
45
- </div>
46
- );
47
- }
48
- );
49
-
50
- CitedExcerpt.displayName = "CitedExcerpt";
51
- export default CitedExcerpt;
@@ -1,2 +0,0 @@
1
- export { CitedExcerpt, type CitedExcerptProps } from "./CitedExcerpt";
2
- export { default } from "./CitedExcerpt";
@@ -1,66 +0,0 @@
1
- /* ComparisonTable — @pcoi/components */
2
-
3
- .pcoi-comparison {
4
- max-width: var(--pcoi-semantic-sizing-comparison-width, 900px);
5
- border: 1px solid var(--pcoi-semantic-border-card);
6
- border-radius: var(--pcoi-semantic-radius-card);
7
- overflow: hidden;
8
- }
9
-
10
- .pcoi-comparison__table {
11
- width: 100%;
12
- border-collapse: collapse;
13
- }
14
-
15
- .pcoi-comparison__header .pcoi-comparison__col {
16
- font-family: var(--pcoi-semantic-type-mono-font);
17
- font-size: var(--pcoi-semantic-type-body-sm-size);
18
- font-weight: var(--pcoi-semantic-type-emphasis-weight);
19
- letter-spacing: var(--pcoi-semantic-type-label-letter-spacing);
20
- text-transform: uppercase;
21
- color: var(--pcoi-semantic-text-secondary);
22
- padding: var(--pcoi-semantic-spacing-stack-md) var(--pcoi-semantic-spacing-card-gap);
23
- background: var(--pcoi-semantic-bg-alt);
24
- text-align: left;
25
- }
26
-
27
- .pcoi-comparison__row {
28
- border-top: 1px solid var(--pcoi-semantic-border-card);
29
- }
30
-
31
- .pcoi-comparison__col {
32
- padding: var(--pcoi-semantic-spacing-stack-md) var(--pcoi-semantic-spacing-card-gap);
33
- font-size: var(--pcoi-semantic-type-body-compact-size);
34
- color: var(--pcoi-semantic-text-secondary);
35
- line-height: var(--pcoi-semantic-type-body-compact-line-height);
36
- }
37
-
38
- .pcoi-comparison__col--label {
39
- font-weight: var(--pcoi-semantic-type-label-weight);
40
- color: var(--pcoi-semantic-text-primary);
41
- text-align: left;
42
- }
43
-
44
- .pcoi-comparison__col--highlight {
45
- background: var(--pcoi-semantic-surface-highlight);
46
- color: var(--pcoi-semantic-text-accent);
47
- font-weight: var(--pcoi-semantic-type-label-weight);
48
- }
49
-
50
- .pcoi-comparison__header .pcoi-comparison__col--highlight {
51
- color: var(--pcoi-semantic-text-accent);
52
- font-weight: var(--pcoi-semantic-type-emphasis-weight);
53
- }
54
-
55
- /* ── Responsive ── */
56
- /* 768px = mobile breakpoint (CSS vars are not supported in @media) */
57
- @media (max-width: 768px) {
58
- .pcoi-comparison {
59
- overflow-x: auto;
60
- -webkit-overflow-scrolling: touch;
61
- }
62
-
63
- .pcoi-comparison__table {
64
- min-width: var(--pcoi-semantic-sizing-comparison-scroll-width);
65
- }
66
- }
@@ -1,48 +0,0 @@
1
- import React from "react";
2
-
3
- export interface ComparisonRow {
4
- label: string;
5
- competitor: string;
6
- pcoi: string;
7
- }
8
-
9
- export interface ComparisonTableProps extends React.HTMLAttributes<HTMLDivElement> {
10
- competitorName?: string;
11
- pcoiName?: string;
12
- rows: ComparisonRow[];
13
- }
14
-
15
- /**
16
- * PCOI Comparison Table
17
- * Semantic <table> with 3 columns: label | competitor | PCOI (highlighted)
18
- * Tokens: radius-md, border/default, bg/alt, surface/accent-dim, text/accent
19
- */
20
- export const ComparisonTable = React.forwardRef<HTMLDivElement, ComparisonTableProps>(
21
- ({ competitorName = "Typical SaaS AI", pcoiName = "PCOI", rows, className = "", ...rest }, ref) => (
22
- <div ref={ref} className={`pcoi-comparison ${className}`} {...rest}>
23
- <table className="pcoi-comparison__table">
24
- <thead>
25
- <tr className="pcoi-comparison__header">
26
- <th className="pcoi-comparison__col" scope="col">
27
- <span className="pcoi-sr-only">Feature</span>
28
- </th>
29
- <th className="pcoi-comparison__col" scope="col">{competitorName}</th>
30
- <th className="pcoi-comparison__col pcoi-comparison__col--highlight" scope="col">{pcoiName}</th>
31
- </tr>
32
- </thead>
33
- <tbody>
34
- {rows.map((row, i) => (
35
- <tr key={i} className="pcoi-comparison__row">
36
- <th className="pcoi-comparison__col pcoi-comparison__col--label" scope="row">{row.label}</th>
37
- <td className="pcoi-comparison__col">{row.competitor}</td>
38
- <td className="pcoi-comparison__col pcoi-comparison__col--highlight">{row.pcoi}</td>
39
- </tr>
40
- ))}
41
- </tbody>
42
- </table>
43
- </div>
44
- )
45
- );
46
-
47
- ComparisonTable.displayName = "ComparisonTable";
48
- export default ComparisonTable;
@@ -1 +0,0 @@
1
- export { ComparisonTable, type ComparisonTableProps, type ComparisonRow } from "./ComparisonTable";
@@ -1,38 +0,0 @@
1
- /* ContactForm — @pcoi/components */
2
-
3
- .pcoi-form {
4
- background: var(--pcoi-semantic-bg-surface);
5
- border: 1px solid var(--pcoi-semantic-border-default);
6
- border-radius: var(--pcoi-semantic-radius-panel);
7
- padding: var(--pcoi-semantic-spacing-form-padding);
8
- display: flex;
9
- flex-direction: column;
10
- gap: var(--pcoi-semantic-spacing-form-gap);
11
- }
12
-
13
- .pcoi-form__row {
14
- display: grid;
15
- grid-template-columns: 1fr 1fr;
16
- gap: var(--pcoi-semantic-spacing-form-gap);
17
- }
18
-
19
- .pcoi-form__full {
20
- grid-column: 1 / -1;
21
- }
22
-
23
- .pcoi-form__note {
24
- font-size: var(--pcoi-semantic-type-label-size);
25
- color: var(--pcoi-semantic-text-muted);
26
- margin: 0;
27
- }
28
-
29
- /* ── Responsive ── */
30
- @media (max-width: 768px) {
31
- .pcoi-form {
32
- padding: var(--pcoi-semantic-spacing-panel-padding);
33
- }
34
-
35
- .pcoi-form__row {
36
- grid-template-columns: 1fr;
37
- }
38
- }
@@ -1,57 +0,0 @@
1
- import React, { useState } from "react";
2
- import { Button } from "../Button";
3
- import { FormField } from "../FormField";
4
-
5
- export interface ContactFormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
6
- onSubmit?: (data: Record<string, string>) => void;
7
- }
8
-
9
- /**
10
- * PCOI Contact Form
11
- * Composes: FormField (atom), Button (atom)
12
- * Tokens: bg/surface, border/default, radius-lg, spacing-40 (padding), spacing-20 (gap)
13
- */
14
- export const ContactForm = React.forwardRef<HTMLFormElement, ContactFormProps>(
15
- ({ onSubmit, className = "", ...rest }, ref) => {
16
- const [submitted, setSubmitted] = useState(false);
17
-
18
- const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
19
- e.preventDefault();
20
- const form = e.currentTarget;
21
- const formData = new FormData(form);
22
- const data = Object.fromEntries(formData.entries()) as Record<string, string>;
23
- onSubmit?.(data);
24
- setSubmitted(true);
25
- form.reset();
26
- setTimeout(() => setSubmitted(false), 3000);
27
- };
28
-
29
- return (
30
- <form ref={ref} className={`pcoi-form ${className}`} onSubmit={handleSubmit} aria-label="Contact form" {...rest}>
31
- <div className="pcoi-form__row">
32
- <FormField label="Name" name="name" placeholder="Your name" required />
33
- <FormField label="Company" name="company" placeholder="Company name" required />
34
- </div>
35
- <div className="pcoi-form__row">
36
- <FormField label="Email" name="email" type="email" placeholder="you@company.com" required />
37
- <FormField label="Your Role" name="role" placeholder="CEO, COO, VP Ops, etc." />
38
- </div>
39
- <FormField
40
- label="What problem are you trying to solve?"
41
- name="context"
42
- multiline
43
- rows={3}
44
- placeholder="Tell us about the knowledge challenges in your organization..."
45
- className="pcoi-form__full"
46
- />
47
- <Button type="submit" variant="primary" size="large" disabled={submitted}>
48
- {submitted ? "Message Received" : "Start the Conversation"}
49
- </Button>
50
- <p className="pcoi-form__note">No sales automation. A real person reads this and responds personally.</p>
51
- </form>
52
- );
53
- }
54
- );
55
-
56
- ContactForm.displayName = "ContactForm";
57
- export default ContactForm;
@@ -1 +0,0 @@
1
- export { ContactForm, type ContactFormProps } from "./ContactForm";
@@ -1,56 +0,0 @@
1
- /* DataTable — @pcoi/components */
2
-
3
- .pcoi-data-table {
4
- width: 100%;
5
- overflow: hidden;
6
- overflow-x: auto;
7
- border: 1px solid var(--pcoi-semantic-border-card);
8
- border-radius: var(--pcoi-semantic-radius-card);
9
- }
10
-
11
- .pcoi-data-table__table {
12
- width: 100%;
13
- border-collapse: collapse;
14
- }
15
-
16
- .pcoi-data-table__th {
17
- font-family: var(--pcoi-semantic-type-mono-font);
18
- font-size: var(--pcoi-semantic-type-body-sm-size);
19
- font-weight: var(--pcoi-semantic-type-emphasis-weight);
20
- letter-spacing: var(--pcoi-semantic-type-label-letter-spacing);
21
- text-transform: uppercase;
22
- color: var(--pcoi-semantic-text-secondary);
23
- text-align: left;
24
- padding: var(--pcoi-semantic-spacing-stack-md) var(--pcoi-semantic-spacing-card-gap);
25
- background: var(--pcoi-semantic-bg-alt);
26
- }
27
-
28
- .pcoi-data-table__th--center { text-align: center; }
29
- .pcoi-data-table__th--right { text-align: right; }
30
-
31
- .pcoi-data-table__td {
32
- font-size: var(--pcoi-semantic-type-body-compact-size);
33
- color: var(--pcoi-semantic-text-secondary);
34
- line-height: var(--pcoi-semantic-type-body-compact-line-height);
35
- text-align: left;
36
- padding: var(--pcoi-semantic-spacing-stack-md) var(--pcoi-semantic-spacing-card-gap);
37
- }
38
-
39
- .pcoi-data-table__td--center { text-align: center; }
40
- .pcoi-data-table__td--right { text-align: right; }
41
-
42
- .pcoi-data-table__row {
43
- border-top: 1px solid var(--pcoi-semantic-border-card);
44
- transition: background var(--pcoi-effect-transition-fast, 0.2s ease);
45
- }
46
-
47
- .pcoi-data-table__row:hover {
48
- background: var(--pcoi-semantic-bg-card-hover);
49
- }
50
-
51
- .pcoi-data-table__empty {
52
- font-size: var(--pcoi-semantic-type-body-size);
53
- color: var(--pcoi-semantic-text-muted);
54
- text-align: center;
55
- padding: var(--pcoi-semantic-spacing-component-gap) var(--pcoi-semantic-spacing-stack-md);
56
- }
@@ -1,104 +0,0 @@
1
- import React from "react";
2
-
3
- export interface DataTableColumn<T = Record<string, unknown>> {
4
- /** Unique key matching a property on the row data */
5
- key: string;
6
- /** Column header text */
7
- header: string;
8
- /** Text alignment (default: "left") */
9
- align?: "left" | "center" | "right";
10
- /** Custom cell renderer */
11
- render?: (value: unknown, row: T) => React.ReactNode;
12
- }
13
-
14
- export interface DataTableProps<T = Record<string, unknown>> extends React.HTMLAttributes<HTMLDivElement> {
15
- /** Column definitions */
16
- columns: DataTableColumn<T>[];
17
- /** Row data */
18
- rows: T[];
19
- /** Key extractor for row identity */
20
- rowKey?: keyof T | ((row: T, index: number) => string);
21
- /** Empty state message */
22
- emptyText?: string;
23
- /** Accessible label for the table (describes its content for screen readers) */
24
- ariaLabel?: string;
25
- }
26
-
27
- /**
28
- * PCOI DataTable — Presentational data table for dashboards
29
- * Tokens: bg/card-hover, border/default, text/primary, text/secondary,
30
- * font-size/caption/body-default, spacing-12/16
31
- */
32
- export const DataTable = React.forwardRef<HTMLDivElement, DataTableProps>(
33
- (
34
- {
35
- columns,
36
- rows,
37
- rowKey,
38
- emptyText = "No data available",
39
- ariaLabel,
40
- className = "",
41
- ...rest
42
- },
43
- ref
44
- ) => {
45
- const getRowKey = (row: Record<string, unknown>, index: number): string => {
46
- if (!rowKey) return String(index);
47
- if (typeof rowKey === "function") return rowKey(row, index);
48
- return String(row[rowKey as string]);
49
- };
50
-
51
- return (
52
- <div ref={ref} className={`pcoi-data-table ${className}`} {...rest}>
53
- <table className="pcoi-data-table__table" aria-label={ariaLabel}>
54
- <thead className="pcoi-data-table__head">
55
- <tr className="pcoi-data-table__head-row">
56
- {columns.map((col) => (
57
- <th
58
- key={col.key}
59
- className={`pcoi-data-table__th${col.align ? ` pcoi-data-table__th--${col.align}` : ""}`}
60
- >
61
- {col.header}
62
- </th>
63
- ))}
64
- </tr>
65
- </thead>
66
- <tbody className="pcoi-data-table__body">
67
- {rows.length === 0 ? (
68
- <tr>
69
- <td
70
- colSpan={columns.length}
71
- className="pcoi-data-table__empty"
72
- >
73
- {emptyText}
74
- </td>
75
- </tr>
76
- ) : (
77
- rows.map((row, index) => (
78
- <tr
79
- key={getRowKey(row as Record<string, unknown>, index)}
80
- className="pcoi-data-table__row"
81
- >
82
- {columns.map((col) => {
83
- const value = (row as Record<string, unknown>)[col.key];
84
- return (
85
- <td
86
- key={col.key}
87
- className={`pcoi-data-table__td${col.align ? ` pcoi-data-table__td--${col.align}` : ""}`}
88
- >
89
- {col.render ? col.render(value, row) : String(value ?? "")}
90
- </td>
91
- );
92
- })}
93
- </tr>
94
- ))
95
- )}
96
- </tbody>
97
- </table>
98
- </div>
99
- );
100
- }
101
- );
102
-
103
- DataTable.displayName = "DataTable";
104
- export default DataTable;
@@ -1,2 +0,0 @@
1
- export { DataTable, type DataTableProps, type DataTableColumn } from "./DataTable";
2
- export { default } from "./DataTable";