Skip to content

Commit 8beb5d6

Browse files
authored
Add input elements to ContentKit (#2882)
1 parent 53f5dbe commit 8beb5d6

File tree

8 files changed

+139
-23
lines changed

8 files changed

+139
-23
lines changed

.changeset/violet-penguins-drum.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@gitbook/react-contentkit': patch
3+
'gitbook': patch
4+
---
5+
6+
Add input elements to ContentKit

bun.lock

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
},
3939
"packages/gitbook": {
4040
"name": "gitbook",
41-
"version": "0.6.3",
41+
"version": "0.6.4",
4242
"dependencies": {
4343
"@gitbook/api": "0.96.1",
4444
"@gitbook/cache-do": "workspace:*",
@@ -118,7 +118,7 @@
118118
},
119119
"packages/gitbook-v2": {
120120
"name": "gitbook-v2",
121-
"version": "0.1.0",
121+
"version": "0.1.1",
122122
"dependencies": {
123123
"@gitbook/api": "0.96.1",
124124
"@sindresorhus/fnv1a": "^3.1.0",
@@ -138,7 +138,7 @@
138138
"name": "@gitbook/icons",
139139
"version": "0.1.0",
140140
"bin": {
141-
"gitbook-icons": "./bin/gitbook-icons.js",
141+
"gitbook-icons": "./bin/gitbook-icons.js"
142142
},
143143
"dependencies": {
144144
"@fortawesome/fontawesome-free": "^6.6.0",
@@ -156,7 +156,7 @@
156156
},
157157
"packages/openapi-parser": {
158158
"name": "@gitbook/openapi-parser",
159-
"version": "2.0.0",
159+
"version": "2.0.1",
160160
"dependencies": {
161161
"@scalar/openapi-parser": "^0.10.4",
162162
"@scalar/openapi-types": "^0.1.6",
@@ -181,6 +181,7 @@
181181
"version": "0.6.0",
182182
"dependencies": {
183183
"@gitbook/api": "0.96.1",
184+
"@gitbook/icons": "workspace:*",
184185
"assert-never": "^1.2.1",
185186
"classnames": "^2.5.1",
186187
},
@@ -195,7 +196,7 @@
195196
"name": "@gitbook/react-math",
196197
"version": "0.6.0",
197198
"bin": {
198-
"gitbook-math": "./bin/gitbook-math.js",
199+
"gitbook-math": "./bin/gitbook-math.js"
199200
},
200201
"dependencies": {
201202
"object-hash": "^3.0.0",
@@ -210,7 +211,7 @@
210211
},
211212
"packages/react-openapi": {
212213
"name": "@gitbook/react-openapi",
213-
"version": "1.0.3",
214+
"version": "1.0.4",
214215
"dependencies": {
215216
"@gitbook/openapi-parser": "workspace:*",
216217
"@scalar/api-client-react": "1.0.87",

packages/gitbook/src/components/DocumentView/Integration/contentkit.css

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
@apply flex gap-4;
44
}
55

6+
/** Add margin bottom to all stacks that are not the last child and have a form */
7+
.contentkit-stack:not(:last-child):has(.contentkit-form) {
8+
@apply mb-4;
9+
}
10+
611
.contentkit-hstack {
7-
@apply flex-row gap-4 items-center;
12+
@apply flex-row gap-3 items-center;
813
}
914
.contentkit-hstack.contentkit-stack-align-center {
1015
@apply justify-center;
@@ -88,17 +93,23 @@
8893

8994
/** Buttons */
9095
.contentkit-button {
91-
@apply flex gap-2 items-center justify-center px-2 py-1 rounded
92-
border border-tint hover:border-tint-hover
93-
text-tint hover:text-tint-strong;
96+
@apply text-sm px-3 h-8 text-center inline-block py-1.5 rounded-md straight-corners:rounded-none place-self-start ring-1 ring-tint hover:ring-tint-hover shadow-sm shadow-tint dark:shadow-tint-1 hover:shadow-md active:shadow-none contrast-more:ring-tint-12 contrast-more:hover:ring-2 contrast-more:hover:ring-tint-12 hover:scale-105 active:scale-100 transition-all grow-0 shrink-0 truncate w-full;
97+
}
98+
99+
.contentkit-button-style-primary {
100+
@apply bg-primary-solid text-contrast-primary-solid hover:bg-primary-solid-hover hover:text-contrast-primary-solid-hover ring-0 contrast-more:ring-1;
101+
}
102+
103+
.contentkit-button-style-secondary {
104+
@apply bg-tint text-tint hover:bg-tint-hover hover:text-primary contrast-more:bg-tint-subtle;
94105
}
95106

96107
.contentkit-button .contentkit-icon {
97108
@apply w-4 h-4;
98109
}
99110

100-
.contentkit-button .contentkit-button-label {
101-
@apply text-sm font-medium text-tint-strong;
111+
.contentkit-button-loading {
112+
@apply size-5 flex mx-auto animate-spin;
102113
}
103114

104115
/** Modals */
@@ -143,7 +154,21 @@
143154

144155
/** Text input */
145156
.contentkit-textinput {
146-
@apply w-full px-2 py-1 rounded border border-tint text-sm text-tint;
157+
@apply w-full rounded border border-tint text-tint-strong placeholder:text-tint flex resize-none flex-1 px-2 py-1.5 text-sm bg-transparent whitespace-pre-line;
158+
@apply focus:outline-primary focus:border-primary;
159+
}
160+
161+
/** Form */
162+
.contentkit-form {
163+
@apply flex flex-col gap-1;
164+
}
165+
166+
.contentkit-form:not(:first-of-type) {
167+
@apply mt-4;
168+
}
169+
170+
.contentkit-label {
171+
@apply text-sm font-medium text-tint-strong;
147172
}
148173

149174
/** Dividers */

packages/react-contentkit/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"dependencies": {
1212
"classnames": "^2.5.1",
1313
"@gitbook/api": "0.96.1",
14-
"assert-never": "^1.2.1"
14+
"assert-never": "^1.2.1",
15+
"@gitbook/icons": "workspace:*"
1516
},
1617
"peerDependencies": {
1718
"react": "*"

packages/react-contentkit/src/Element.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ElementCodeBlock } from './ElementCodeBlock';
1313
import { ElementDivider } from './ElementDivider';
1414
import { ElementIcon } from './ElementIcon';
1515
import { ElementImage } from './ElementImage';
16+
import { ElementInput } from './ElementInput';
1617
import { ElementMarkdown } from './ElementMarkdown';
1718
import { ElementModal } from './ElementModal';
1819
import { ElementStack } from './ElementStack';
@@ -152,6 +153,8 @@ export function Element(props: {
152153
return <ElementCodeBlock element={element} context={context} state={state} />;
153154
case 'webframe':
154155
return <ElementWebframe element={element} />;
156+
case 'input':
157+
return <ElementInput element={element} context={context} state={state} />;
155158
default:
156159
return (
157160
<pre

packages/react-contentkit/src/ElementButton.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client';
22

33
import type { ContentKitButton } from '@gitbook/api';
4+
import { Icon } from '@gitbook/icons';
45
import React from 'react';
56

67
import classNames from 'classnames';
@@ -19,16 +20,14 @@ export function ElementButton(
1920
const [loading, setLoading] = React.useState(false);
2021

2122
// TODO:
22-
// - loading
23-
// - confirm
23+
// - confirm dialog
2424

2525
return (
2626
<button
2727
title={element.tooltip}
2828
className={classNames(
2929
'contentkit-button',
30-
`contentkit-button-style-${element.style ?? 'secondary'}`,
31-
loading ? 'contentkit-button-loading' : null
30+
`contentkit-button-style-${element.style ?? 'secondary'}`
3231
)}
3332
onClick={(event) => {
3433
if (element.disabled || loading) {
@@ -43,11 +42,17 @@ export function ElementButton(
4342
});
4443
}}
4544
>
46-
{icon}
47-
{element.label ? (
48-
<span className="contentkit-button-label">{element.label}</span>
49-
) : null}
50-
{trailingIcon}
45+
{loading ? (
46+
<Icon icon="spinner" className="contentkit-button-loading" />
47+
) : (
48+
<>
49+
{icon}
50+
{element.label ? (
51+
<span className="contentkit-button-label">{element.label}</span>
52+
) : null}
53+
{trailingIcon}
54+
</>
55+
)}
5156
</button>
5257
);
5358
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import type {
2+
ContentKitButton,
3+
ContentKitCheckbox,
4+
ContentKitCodeBlock,
5+
ContentKitInput,
6+
ContentKitRadio,
7+
ContentKitSelect,
8+
ContentKitSwitch,
9+
ContentKitTextInput,
10+
} from '@gitbook/api';
11+
import classNames from 'classnames';
12+
import { ElementButton } from './ElementButton';
13+
import { ElementCodeBlock } from './ElementCodeBlock';
14+
import { ElementIcon } from './ElementIcon';
15+
import { ElementTextInput } from './ElementTextInput';
16+
import type { ContentKitServerContext } from './types';
17+
18+
export function ElementInput(props: {
19+
element: ContentKitInput;
20+
context: ContentKitServerContext;
21+
state: object;
22+
}) {
23+
const { element, context, state } = props;
24+
const { label, element: childElement } = element;
25+
26+
return (
27+
<div className={classNames('contentkit-form')}>
28+
{label ? <label className="contentkit-label">{label}</label> : null}
29+
{getInputElement({ element: childElement, context, state })}
30+
</div>
31+
);
32+
}
33+
34+
function getInputElement(props: {
35+
element:
36+
| ContentKitTextInput
37+
| ContentKitSelect
38+
| ContentKitSwitch
39+
| ContentKitRadio
40+
| ContentKitCheckbox
41+
| ContentKitButton
42+
| ContentKitCodeBlock;
43+
context: ContentKitServerContext;
44+
state: object;
45+
}) {
46+
const { element, context, state } = props;
47+
48+
//!!TODO add support for:
49+
// - select
50+
// - switch
51+
// - radio
52+
// - checkbox
53+
54+
switch (element.type) {
55+
case 'textinput':
56+
return <ElementTextInput element={element} />;
57+
case 'button':
58+
return (
59+
<ElementButton
60+
element={element}
61+
icon={
62+
element.icon ? <ElementIcon icon={element.icon} context={context} /> : null
63+
}
64+
trailingIcon={
65+
element.trailingIcon ? (
66+
<ElementIcon icon={element.trailingIcon} context={context} />
67+
) : null
68+
}
69+
/>
70+
);
71+
case 'codeblock':
72+
return <ElementCodeBlock element={element} context={context} state={state} />;
73+
}
74+
}

packages/react-contentkit/src/ElementTextInput.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function ElementTextInput(props: ContentKitClientElementProps<ContentKitT
2929
value={value}
3030
placeholder={element.placeholder}
3131
onChange={onChange}
32+
rows={4}
3233
/>
3334
);
3435
}

0 commit comments

Comments
 (0)