@sproutsocial/seeds-react-narrative-kit 0.1.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/.eslintignore +3 -0
- package/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +21 -0
- package/CHANGELOG.md +9 -0
- package/README.md +52 -0
- package/dist/esm/index.js +40 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +36 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/narrative-container.css +56 -0
- package/jest.config.js +9 -0
- package/package.json +47 -0
- package/src/NarrativeContainer/NarrativeContainer.stories.tsx +37 -0
- package/src/NarrativeContainer/NarrativeContainer.tsx +57 -0
- package/src/NarrativeContainer/NarrativeContainerTypes.ts +12 -0
- package/src/NarrativeContainer/__tests__/NarrativeContainer.test.tsx +33 -0
- package/src/NarrativeContainer/index.ts +2 -0
- package/src/_internal/types.ts +16 -0
- package/src/index.ts +8 -0
- package/src/narrative-container.css +56 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +12 -0
package/.eslintignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
yarn run v1.22.22
|
|
2
|
+
$ tsup --dts && cp src/narrative-container.css dist/narrative-container.css
|
|
3
|
+
[34mCLI[39m Building entry: src/index.ts
|
|
4
|
+
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
5
|
+
[34mCLI[39m tsup v8.5.0
|
|
6
|
+
[34mCLI[39m Using tsup config: /home/runner/_work/seeds/seeds/seeds-react/seeds-react-narrative-kit/tsup.config.ts
|
|
7
|
+
[34mCLI[39m Target: es2022
|
|
8
|
+
[34mCLI[39m Cleaning output folder
|
|
9
|
+
[34mCJS[39m Build start
|
|
10
|
+
[34mESM[39m Build start
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m2.65 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m2.72 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 93ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m1.03 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m2.35 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 93ms
|
|
17
|
+
[34mDTS[39m Build start
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 4507ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.45 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.45 KB[39m
|
|
21
|
+
Done in 6.42s.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @sproutsocial/seeds-react-narrative-kit
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5d581a6: Add `seeds-react-narrative-kit` — a kit of composable narrative primitives for
|
|
8
|
+
executive-brief style pages. Ships the foundational `NarrativeContainer` surface and is
|
|
9
|
+
exported through `@sproutsocial/racine`.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @sproutsocial/seeds-react-narrative-kit
|
|
2
|
+
|
|
3
|
+
A kit of **narrative primitives** — small, composable building blocks that are assembled
|
|
4
|
+
(often non-deterministically, by agents) into "executive brief" style pages: a surface, a
|
|
5
|
+
headline stat, a callout, a trend, and so on.
|
|
6
|
+
|
|
7
|
+
This package is a multi-component kit, not a single component. Each primitive lives in its
|
|
8
|
+
own folder under `src/` and is re-exported from the package barrel (`src/index.ts`).
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
Like all Seeds React components, narrative primitives are consumed through
|
|
13
|
+
`@sproutsocial/racine` — never imported from this package directly in a consuming app:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { NarrativeContainer } from "@sproutsocial/racine";
|
|
17
|
+
|
|
18
|
+
<NarrativeContainer>{/* narrative content */}</NarrativeContainer>;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Exported primitives
|
|
22
|
+
|
|
23
|
+
| Primitive | Description |
|
|
24
|
+
| -------------------- | ------------------------------------------------------------------------------------ |
|
|
25
|
+
| `NarrativeContainer` | Foundational surface/card with a content slot and a top-left gradient accent border. Accepts standard `className` / `style`. |
|
|
26
|
+
|
|
27
|
+
## Adding a new primitive
|
|
28
|
+
|
|
29
|
+
Primitives are intentionally cheap to add — a new one is a folder plus one barrel line:
|
|
30
|
+
|
|
31
|
+
1. Create `src/<PrimitiveName>/` with:
|
|
32
|
+
- `<PrimitiveName>.tsx` — `forwardRef` component (set `displayName`).
|
|
33
|
+
- `<PrimitiveName>Types.ts` — the public `Type<PrimitiveName>Props` interface (JSDoc each prop).
|
|
34
|
+
- `<PrimitiveName>.stories.tsx` — `title: "Narrative Kit/<PrimitiveName>"`.
|
|
35
|
+
- `__tests__/<PrimitiveName>.test.tsx`.
|
|
36
|
+
- `index.ts` — re-export the component and its types.
|
|
37
|
+
2. Add one line to `src/index.ts`:
|
|
38
|
+
```ts
|
|
39
|
+
export { PrimitiveName, type TypePrimitiveNameProps } from "./PrimitiveName";
|
|
40
|
+
```
|
|
41
|
+
3. Add a row to the table above.
|
|
42
|
+
|
|
43
|
+
Shared types/helpers used across primitives live in `src/_internal/`.
|
|
44
|
+
|
|
45
|
+
## Development
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
yarn build # bundle (tsup) cjs + esm + .d.ts
|
|
49
|
+
yarn typecheck # tsc --noEmit
|
|
50
|
+
yarn test # jest
|
|
51
|
+
yarn lint
|
|
52
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/NarrativeContainer/NarrativeContainer.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
function cn(...inputs) {
|
|
5
|
+
const classes = [];
|
|
6
|
+
for (const input of inputs) {
|
|
7
|
+
if (!input) continue;
|
|
8
|
+
if (typeof input === "string") {
|
|
9
|
+
classes.push(input);
|
|
10
|
+
} else if (typeof input === "object") {
|
|
11
|
+
for (const [key, value] of Object.entries(input)) {
|
|
12
|
+
if (value) {
|
|
13
|
+
classes.push(key);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return classes.join(" ");
|
|
19
|
+
}
|
|
20
|
+
var NarrativeContainer = React.forwardRef(({ children, accent = true, className, ...props }, ref) => {
|
|
21
|
+
return /* @__PURE__ */ jsx(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
ref,
|
|
25
|
+
className: cn(
|
|
26
|
+
"seeds-narrative-container",
|
|
27
|
+
{ "seeds-narrative-container-accent": accent },
|
|
28
|
+
className
|
|
29
|
+
),
|
|
30
|
+
...props,
|
|
31
|
+
children
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
NarrativeContainer.displayName = "NarrativeContainer";
|
|
36
|
+
var NarrativeContainer_default = NarrativeContainer;
|
|
37
|
+
export {
|
|
38
|
+
NarrativeContainer_default as NarrativeContainer
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/NarrativeContainer/NarrativeContainer.tsx"],"sourcesContent":["import * as React from \"react\";\nimport type { TypeNarrativeContainerProps } from \"./NarrativeContainerTypes\";\n\n/** Merge class names, supporting `{ \"class\": boolean }` toggles. */\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\n/**\n * NarrativeContainer — the foundational surface that narrative primitives are\n * composed inside. A padded, rounded, elevated card with a content slot and an\n * optional decorative gradient accent that wraps the top-left corner.\n *\n * Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers\n * import the classes through `@sproutsocial/racine/css/components`. Surface\n * overrides are done with standard `className` / `style`.\n */\nconst NarrativeContainer = React.forwardRef<\n HTMLDivElement,\n TypeNarrativeContainerProps\n>(({ children, accent = true, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n \"seeds-narrative-container\",\n { \"seeds-narrative-container-accent\": accent },\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nNarrativeContainer.displayName = \"NarrativeContainer\";\n\nexport default NarrativeContainer;\n"],"mappings":";AAAA,YAAY,WAAW;AAwCnB;AApCJ,SAAS,MACJ,QACK;AACR,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAO;AAEZ,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,KAAK,KAAK;AAAA,IACpB,WAAW,OAAO,UAAU,UAAU;AACpC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,OAAO;AACT,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;AAWA,IAAM,qBAA2B,iBAG/B,CAAC,EAAE,UAAU,SAAS,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,EAAE,oCAAoC,OAAO;AAAA,QAC7C;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ,CAAC;AAED,mBAAmB,cAAc;AAEjC,IAAO,6BAAQ;","names":[]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface TypeNarrativeContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
/** Narrative content rendered inside the container's slot. */
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* Render the decorative gradient accent that wraps the top-left corner.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
accent?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* NarrativeContainer — the foundational surface that narrative primitives are
|
|
15
|
+
* composed inside. A padded, rounded, elevated card with a content slot and an
|
|
16
|
+
* optional decorative gradient accent that wraps the top-left corner.
|
|
17
|
+
*
|
|
18
|
+
* Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers
|
|
19
|
+
* import the classes through `@sproutsocial/racine/css/components`. Surface
|
|
20
|
+
* overrides are done with standard `className` / `style`.
|
|
21
|
+
*/
|
|
22
|
+
declare const NarrativeContainer: React.ForwardRefExoticComponent<TypeNarrativeContainerProps & React.RefAttributes<HTMLDivElement>>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shared types for narrative primitives.
|
|
26
|
+
*
|
|
27
|
+
* Kept intentionally small — primitives built in follow-up work extend this as
|
|
28
|
+
* cross-cutting concerns emerge (tone/sentiment, density, etc.).
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Sentiment/tone shared across narrative primitives, used to drive decorative
|
|
32
|
+
* accents (e.g. the NarrativeContainer gradient edge) and semantic coloring.
|
|
33
|
+
*/
|
|
34
|
+
type TypeNarrativeTone = "neutral" | "positive" | "negative" | "opportunity";
|
|
35
|
+
|
|
36
|
+
export { NarrativeContainer, type TypeNarrativeContainerProps, type TypeNarrativeTone };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface TypeNarrativeContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
/** Narrative content rendered inside the container's slot. */
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* Render the decorative gradient accent that wraps the top-left corner.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
accent?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* NarrativeContainer — the foundational surface that narrative primitives are
|
|
15
|
+
* composed inside. A padded, rounded, elevated card with a content slot and an
|
|
16
|
+
* optional decorative gradient accent that wraps the top-left corner.
|
|
17
|
+
*
|
|
18
|
+
* Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers
|
|
19
|
+
* import the classes through `@sproutsocial/racine/css/components`. Surface
|
|
20
|
+
* overrides are done with standard `className` / `style`.
|
|
21
|
+
*/
|
|
22
|
+
declare const NarrativeContainer: React.ForwardRefExoticComponent<TypeNarrativeContainerProps & React.RefAttributes<HTMLDivElement>>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shared types for narrative primitives.
|
|
26
|
+
*
|
|
27
|
+
* Kept intentionally small — primitives built in follow-up work extend this as
|
|
28
|
+
* cross-cutting concerns emerge (tone/sentiment, density, etc.).
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Sentiment/tone shared across narrative primitives, used to drive decorative
|
|
32
|
+
* accents (e.g. the NarrativeContainer gradient edge) and semantic coloring.
|
|
33
|
+
*/
|
|
34
|
+
type TypeNarrativeTone = "neutral" | "positive" | "negative" | "opportunity";
|
|
35
|
+
|
|
36
|
+
export { NarrativeContainer, type TypeNarrativeContainerProps, type TypeNarrativeTone };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
NarrativeContainer: () => NarrativeContainer_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/NarrativeContainer/NarrativeContainer.tsx
|
|
38
|
+
var React = __toESM(require("react"));
|
|
39
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
40
|
+
function cn(...inputs) {
|
|
41
|
+
const classes = [];
|
|
42
|
+
for (const input of inputs) {
|
|
43
|
+
if (!input) continue;
|
|
44
|
+
if (typeof input === "string") {
|
|
45
|
+
classes.push(input);
|
|
46
|
+
} else if (typeof input === "object") {
|
|
47
|
+
for (const [key, value] of Object.entries(input)) {
|
|
48
|
+
if (value) {
|
|
49
|
+
classes.push(key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return classes.join(" ");
|
|
55
|
+
}
|
|
56
|
+
var NarrativeContainer = React.forwardRef(({ children, accent = true, className, ...props }, ref) => {
|
|
57
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
ref,
|
|
61
|
+
className: cn(
|
|
62
|
+
"seeds-narrative-container",
|
|
63
|
+
{ "seeds-narrative-container-accent": accent },
|
|
64
|
+
className
|
|
65
|
+
),
|
|
66
|
+
...props,
|
|
67
|
+
children
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
NarrativeContainer.displayName = "NarrativeContainer";
|
|
72
|
+
var NarrativeContainer_default = NarrativeContainer;
|
|
73
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
74
|
+
0 && (module.exports = {
|
|
75
|
+
NarrativeContainer
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/NarrativeContainer/NarrativeContainer.tsx"],"sourcesContent":["/* Narrative Kit — composable narrative primitives.\n * Each primitive adds a single line below. No default export — this is a kit. */\nexport {\n NarrativeContainer,\n type TypeNarrativeContainerProps,\n} from \"./NarrativeContainer\";\n\nexport type { TypeNarrativeTone } from \"./_internal/types\";\n","import * as React from \"react\";\nimport type { TypeNarrativeContainerProps } from \"./NarrativeContainerTypes\";\n\n/** Merge class names, supporting `{ \"class\": boolean }` toggles. */\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\n/**\n * NarrativeContainer — the foundational surface that narrative primitives are\n * composed inside. A padded, rounded, elevated card with a content slot and an\n * optional decorative gradient accent that wraps the top-left corner.\n *\n * Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers\n * import the classes through `@sproutsocial/racine/css/components`. Surface\n * overrides are done with standard `className` / `style`.\n */\nconst NarrativeContainer = React.forwardRef<\n HTMLDivElement,\n TypeNarrativeContainerProps\n>(({ children, accent = true, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n \"seeds-narrative-container\",\n { \"seeds-narrative-container-accent\": accent },\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nNarrativeContainer.displayName = \"NarrativeContainer\";\n\nexport default NarrativeContainer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AAwCnB;AApCJ,SAAS,MACJ,QACK;AACR,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAO;AAEZ,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,KAAK,KAAK;AAAA,IACpB,WAAW,OAAO,UAAU,UAAU;AACpC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,OAAO;AACT,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;AAWA,IAAM,qBAA2B,iBAG/B,CAAC,EAAE,UAAU,SAAS,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,EAAE,oCAAoC,OAAO;AAAA,QAC7C;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ,CAAC;AAED,mBAAmB,cAAc;AAEjC,IAAO,6BAAQ;","names":[]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds NarrativeContainer component classes.
|
|
3
|
+
* Use these instead of writing out individual Tailwind utility classes.
|
|
4
|
+
*
|
|
5
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
6
|
+
* CSS variable definitions and dark mode support.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <div class="seeds-narrative-container seeds-narrative-container-accent">…</div>
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
@layer components {
|
|
13
|
+
|
|
14
|
+
.seeds-narrative-container {
|
|
15
|
+
position: relative;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: flex-start;
|
|
21
|
+
padding: var(--space-500);
|
|
22
|
+
border-radius: var(--radius-800);
|
|
23
|
+
box-shadow: var(--shadow-low);
|
|
24
|
+
background-color: var(--color-container-bg-base);
|
|
25
|
+
font-family: var(--font-family);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Decorative inner gradient border anchored to the top-left corner.
|
|
29
|
+
A masked ::before so the gradient renders as a uniform-width ring that follows
|
|
30
|
+
border-radius and never tints the content. The radial ellipse fades to
|
|
31
|
+
transparent away from the corner; its x-radius (80%) is larger than its
|
|
32
|
+
y-radius (60%) so the accent runs slightly further along the top edge than down
|
|
33
|
+
the left edge. Brand stops mirror --color-gradient-ai — tokenize to a
|
|
34
|
+
--color-gradient-* token once one exists for a fade-to-transparent variant. */
|
|
35
|
+
.seeds-narrative-container-accent::before {
|
|
36
|
+
content: "";
|
|
37
|
+
position: absolute;
|
|
38
|
+
inset: 0;
|
|
39
|
+
border-radius: inherit;
|
|
40
|
+
padding: 4px;
|
|
41
|
+
background: radial-gradient(
|
|
42
|
+
ellipse 80% 60% at top left,
|
|
43
|
+
#205bc3 0%,
|
|
44
|
+
#9747ff 28%,
|
|
45
|
+
#f282f5 50%,
|
|
46
|
+
transparent 72%
|
|
47
|
+
);
|
|
48
|
+
-webkit-mask:
|
|
49
|
+
linear-gradient(#fff 0 0) content-box,
|
|
50
|
+
linear-gradient(#fff 0 0);
|
|
51
|
+
-webkit-mask-composite: xor;
|
|
52
|
+
mask-composite: exclude;
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sproutsocial/seeds-react-narrative-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Seeds React Narrative Kit — composable narrative primitives for executive-brief style pages",
|
|
5
|
+
"author": "Sprout Social, Inc.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/esm/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/esm/index.js",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./dist/narrative-container.css": "./dist/narrative-container.css"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup --dts && cp src/narrative-container.css dist/narrative-container.css",
|
|
20
|
+
"build:debug": "tsup --dts --metafile",
|
|
21
|
+
"dev": "tsup --watch --dts",
|
|
22
|
+
"clean": "rm -rf .turbo dist",
|
|
23
|
+
"clean:modules": "rm -rf node_modules",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"test": "jest",
|
|
26
|
+
"test:watch": "jest --watch --coverage=false"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@sproutsocial/seeds-react-theme": "^4.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@sproutsocial/eslint-config-seeds": "*",
|
|
33
|
+
"@sproutsocial/seeds-react-testing-library": "*",
|
|
34
|
+
"@sproutsocial/seeds-testing": "*",
|
|
35
|
+
"@sproutsocial/seeds-tsconfig": "*",
|
|
36
|
+
"@types/react": "^18.0.0",
|
|
37
|
+
"react": "^18.0.0",
|
|
38
|
+
"tsup": "^8.3.4",
|
|
39
|
+
"typescript": "^5.6.2"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": "^17.0.0 || ^18.0.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import NarrativeContainer from "./NarrativeContainer";
|
|
4
|
+
import "../narrative-container.css";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Narrative Kit/NarrativeContainer",
|
|
8
|
+
component: NarrativeContainer,
|
|
9
|
+
} satisfies Meta<typeof NarrativeContainer>;
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
|
|
14
|
+
const SlotContent = () => (
|
|
15
|
+
<div style={{ display: "grid", gap: 8 }}>
|
|
16
|
+
<strong>Engagement is up 24% this month</strong>
|
|
17
|
+
<span>
|
|
18
|
+
Driven by a strong week on LinkedIn and a viral reply thread. Audience
|
|
19
|
+
growth held steady.
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
style: { width: 384 },
|
|
27
|
+
children: <SlotContent />,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const WithoutAccent: Story = {
|
|
32
|
+
args: {
|
|
33
|
+
style: { width: 384 },
|
|
34
|
+
accent: false,
|
|
35
|
+
children: <SlotContent />,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { TypeNarrativeContainerProps } from "./NarrativeContainerTypes";
|
|
3
|
+
|
|
4
|
+
/** Merge class names, supporting `{ "class": boolean }` toggles. */
|
|
5
|
+
function cn(
|
|
6
|
+
...inputs: (string | undefined | null | false | Record<string, boolean>)[]
|
|
7
|
+
): string {
|
|
8
|
+
const classes: string[] = [];
|
|
9
|
+
|
|
10
|
+
for (const input of inputs) {
|
|
11
|
+
if (!input) continue;
|
|
12
|
+
|
|
13
|
+
if (typeof input === "string") {
|
|
14
|
+
classes.push(input);
|
|
15
|
+
} else if (typeof input === "object") {
|
|
16
|
+
for (const [key, value] of Object.entries(input)) {
|
|
17
|
+
if (value) {
|
|
18
|
+
classes.push(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return classes.join(" ");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* NarrativeContainer — the foundational surface that narrative primitives are
|
|
29
|
+
* composed inside. A padded, rounded, elevated card with a content slot and an
|
|
30
|
+
* optional decorative gradient accent that wraps the top-left corner.
|
|
31
|
+
*
|
|
32
|
+
* Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers
|
|
33
|
+
* import the classes through `@sproutsocial/racine/css/components`. Surface
|
|
34
|
+
* overrides are done with standard `className` / `style`.
|
|
35
|
+
*/
|
|
36
|
+
const NarrativeContainer = React.forwardRef<
|
|
37
|
+
HTMLDivElement,
|
|
38
|
+
TypeNarrativeContainerProps
|
|
39
|
+
>(({ children, accent = true, className, ...props }, ref) => {
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
ref={ref}
|
|
43
|
+
className={cn(
|
|
44
|
+
"seeds-narrative-container",
|
|
45
|
+
{ "seeds-narrative-container-accent": accent },
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
NarrativeContainer.displayName = "NarrativeContainer";
|
|
56
|
+
|
|
57
|
+
export default NarrativeContainer;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
|
|
3
|
+
export interface TypeNarrativeContainerProps
|
|
4
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Narrative content rendered inside the container's slot. */
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Render the decorative gradient accent that wraps the top-left corner.
|
|
9
|
+
* @default true
|
|
10
|
+
*/
|
|
11
|
+
accent?: boolean;
|
|
12
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@sproutsocial/seeds-react-testing-library";
|
|
3
|
+
import NarrativeContainer from "../NarrativeContainer";
|
|
4
|
+
|
|
5
|
+
describe("NarrativeContainer", () => {
|
|
6
|
+
it("renders its slot content", () => {
|
|
7
|
+
render(<NarrativeContainer>Brief summary</NarrativeContainer>);
|
|
8
|
+
|
|
9
|
+
expect(screen.getByText("Brief summary")).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("forwards its ref to the underlying element", () => {
|
|
13
|
+
const ref = React.createRef<HTMLDivElement>();
|
|
14
|
+
render(<NarrativeContainer ref={ref}>Content</NarrativeContainer>);
|
|
15
|
+
|
|
16
|
+
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("renders the gradient accent by default and omits it when disabled", () => {
|
|
20
|
+
// The accent is a decorative CSS ::before toggled by a modifier class.
|
|
21
|
+
const { container, rerender } = render(
|
|
22
|
+
<NarrativeContainer>Content</NarrativeContainer>
|
|
23
|
+
);
|
|
24
|
+
expect(
|
|
25
|
+
container.querySelector(".seeds-narrative-container-accent")
|
|
26
|
+
).toBeInTheDocument();
|
|
27
|
+
|
|
28
|
+
rerender(<NarrativeContainer accent={false}>Content</NarrativeContainer>);
|
|
29
|
+
expect(
|
|
30
|
+
container.querySelector(".seeds-narrative-container-accent")
|
|
31
|
+
).not.toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for narrative primitives.
|
|
3
|
+
*
|
|
4
|
+
* Kept intentionally small — primitives built in follow-up work extend this as
|
|
5
|
+
* cross-cutting concerns emerge (tone/sentiment, density, etc.).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sentiment/tone shared across narrative primitives, used to drive decorative
|
|
10
|
+
* accents (e.g. the NarrativeContainer gradient edge) and semantic coloring.
|
|
11
|
+
*/
|
|
12
|
+
export type TypeNarrativeTone =
|
|
13
|
+
| "neutral"
|
|
14
|
+
| "positive"
|
|
15
|
+
| "negative"
|
|
16
|
+
| "opportunity";
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/* Narrative Kit — composable narrative primitives.
|
|
2
|
+
* Each primitive adds a single line below. No default export — this is a kit. */
|
|
3
|
+
export {
|
|
4
|
+
NarrativeContainer,
|
|
5
|
+
type TypeNarrativeContainerProps,
|
|
6
|
+
} from "./NarrativeContainer";
|
|
7
|
+
|
|
8
|
+
export type { TypeNarrativeTone } from "./_internal/types";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds NarrativeContainer component classes.
|
|
3
|
+
* Use these instead of writing out individual Tailwind utility classes.
|
|
4
|
+
*
|
|
5
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
6
|
+
* CSS variable definitions and dark mode support.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <div class="seeds-narrative-container seeds-narrative-container-accent">…</div>
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
@layer components {
|
|
13
|
+
|
|
14
|
+
.seeds-narrative-container {
|
|
15
|
+
position: relative;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: flex-start;
|
|
21
|
+
padding: var(--space-500);
|
|
22
|
+
border-radius: var(--radius-800);
|
|
23
|
+
box-shadow: var(--shadow-low);
|
|
24
|
+
background-color: var(--color-container-bg-base);
|
|
25
|
+
font-family: var(--font-family);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Decorative inner gradient border anchored to the top-left corner.
|
|
29
|
+
A masked ::before so the gradient renders as a uniform-width ring that follows
|
|
30
|
+
border-radius and never tints the content. The radial ellipse fades to
|
|
31
|
+
transparent away from the corner; its x-radius (80%) is larger than its
|
|
32
|
+
y-radius (60%) so the accent runs slightly further along the top edge than down
|
|
33
|
+
the left edge. Brand stops mirror --color-gradient-ai — tokenize to a
|
|
34
|
+
--color-gradient-* token once one exists for a fade-to-transparent variant. */
|
|
35
|
+
.seeds-narrative-container-accent::before {
|
|
36
|
+
content: "";
|
|
37
|
+
position: absolute;
|
|
38
|
+
inset: 0;
|
|
39
|
+
border-radius: inherit;
|
|
40
|
+
padding: 4px;
|
|
41
|
+
background: radial-gradient(
|
|
42
|
+
ellipse 80% 60% at top left,
|
|
43
|
+
#205bc3 0%,
|
|
44
|
+
#9747ff 28%,
|
|
45
|
+
#f282f5 50%,
|
|
46
|
+
transparent 72%
|
|
47
|
+
);
|
|
48
|
+
-webkit-mask:
|
|
49
|
+
linear-gradient(#fff 0 0) content-box,
|
|
50
|
+
linear-gradient(#fff 0 0);
|
|
51
|
+
-webkit-mask-composite: xor;
|
|
52
|
+
mask-composite: exclude;
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
export default defineConfig((options) => ({
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["cjs", "esm"],
|
|
6
|
+
clean: true,
|
|
7
|
+
legacyOutput: true,
|
|
8
|
+
dts: options.dts,
|
|
9
|
+
external: ["react", /^@sproutsocial\//],
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
metafile: options.metafile,
|
|
12
|
+
}));
|