mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 15:45:13 +00:00
Add input elements to ContentKit (#2882)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@gitbook/react-contentkit': patch
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Add input elements to ContentKit
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"packages/gitbook": {
|
||||
"name": "gitbook",
|
||||
"version": "0.6.3",
|
||||
"version": "0.6.4",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "0.96.1",
|
||||
"@gitbook/cache-do": "workspace:*",
|
||||
@@ -118,7 +118,7 @@
|
||||
},
|
||||
"packages/gitbook-v2": {
|
||||
"name": "gitbook-v2",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "0.96.1",
|
||||
"@sindresorhus/fnv1a": "^3.1.0",
|
||||
@@ -138,7 +138,7 @@
|
||||
"name": "@gitbook/icons",
|
||||
"version": "0.1.0",
|
||||
"bin": {
|
||||
"gitbook-icons": "./bin/gitbook-icons.js",
|
||||
"gitbook-icons": "./bin/gitbook-icons.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
@@ -156,7 +156,7 @@
|
||||
},
|
||||
"packages/openapi-parser": {
|
||||
"name": "@gitbook/openapi-parser",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"dependencies": {
|
||||
"@scalar/openapi-parser": "^0.10.4",
|
||||
"@scalar/openapi-types": "^0.1.6",
|
||||
@@ -181,6 +181,7 @@
|
||||
"version": "0.6.0",
|
||||
"dependencies": {
|
||||
"@gitbook/api": "0.96.1",
|
||||
"@gitbook/icons": "workspace:*",
|
||||
"assert-never": "^1.2.1",
|
||||
"classnames": "^2.5.1",
|
||||
},
|
||||
@@ -195,7 +196,7 @@
|
||||
"name": "@gitbook/react-math",
|
||||
"version": "0.6.0",
|
||||
"bin": {
|
||||
"gitbook-math": "./bin/gitbook-math.js",
|
||||
"gitbook-math": "./bin/gitbook-math.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"object-hash": "^3.0.0",
|
||||
@@ -210,7 +211,7 @@
|
||||
},
|
||||
"packages/react-openapi": {
|
||||
"name": "@gitbook/react-openapi",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"dependencies": {
|
||||
"@gitbook/openapi-parser": "workspace:*",
|
||||
"@scalar/api-client-react": "1.0.87",
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
@apply flex gap-4;
|
||||
}
|
||||
|
||||
/** Add margin bottom to all stacks that are not the last child and have a form */
|
||||
.contentkit-stack:not(:last-child):has(.contentkit-form) {
|
||||
@apply mb-4;
|
||||
}
|
||||
|
||||
.contentkit-hstack {
|
||||
@apply flex-row gap-4 items-center;
|
||||
@apply flex-row gap-3 items-center;
|
||||
}
|
||||
.contentkit-hstack.contentkit-stack-align-center {
|
||||
@apply justify-center;
|
||||
@@ -88,17 +93,23 @@
|
||||
|
||||
/** Buttons */
|
||||
.contentkit-button {
|
||||
@apply flex gap-2 items-center justify-center px-2 py-1 rounded
|
||||
border border-tint hover:border-tint-hover
|
||||
text-tint hover:text-tint-strong;
|
||||
@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;
|
||||
}
|
||||
|
||||
.contentkit-button-style-primary {
|
||||
@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;
|
||||
}
|
||||
|
||||
.contentkit-button-style-secondary {
|
||||
@apply bg-tint text-tint hover:bg-tint-hover hover:text-primary contrast-more:bg-tint-subtle;
|
||||
}
|
||||
|
||||
.contentkit-button .contentkit-icon {
|
||||
@apply w-4 h-4;
|
||||
}
|
||||
|
||||
.contentkit-button .contentkit-button-label {
|
||||
@apply text-sm font-medium text-tint-strong;
|
||||
.contentkit-button-loading {
|
||||
@apply size-5 flex mx-auto animate-spin;
|
||||
}
|
||||
|
||||
/** Modals */
|
||||
@@ -143,7 +154,21 @@
|
||||
|
||||
/** Text input */
|
||||
.contentkit-textinput {
|
||||
@apply w-full px-2 py-1 rounded border border-tint text-sm text-tint;
|
||||
@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;
|
||||
@apply focus:outline-primary focus:border-primary;
|
||||
}
|
||||
|
||||
/** Form */
|
||||
.contentkit-form {
|
||||
@apply flex flex-col gap-1;
|
||||
}
|
||||
|
||||
.contentkit-form:not(:first-of-type) {
|
||||
@apply mt-4;
|
||||
}
|
||||
|
||||
.contentkit-label {
|
||||
@apply text-sm font-medium text-tint-strong;
|
||||
}
|
||||
|
||||
/** Dividers */
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"dependencies": {
|
||||
"classnames": "^2.5.1",
|
||||
"@gitbook/api": "0.96.1",
|
||||
"assert-never": "^1.2.1"
|
||||
"assert-never": "^1.2.1",
|
||||
"@gitbook/icons": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ElementCodeBlock } from './ElementCodeBlock';
|
||||
import { ElementDivider } from './ElementDivider';
|
||||
import { ElementIcon } from './ElementIcon';
|
||||
import { ElementImage } from './ElementImage';
|
||||
import { ElementInput } from './ElementInput';
|
||||
import { ElementMarkdown } from './ElementMarkdown';
|
||||
import { ElementModal } from './ElementModal';
|
||||
import { ElementStack } from './ElementStack';
|
||||
@@ -152,6 +153,8 @@ export function Element(props: {
|
||||
return <ElementCodeBlock element={element} context={context} state={state} />;
|
||||
case 'webframe':
|
||||
return <ElementWebframe element={element} />;
|
||||
case 'input':
|
||||
return <ElementInput element={element} context={context} state={state} />;
|
||||
default:
|
||||
return (
|
||||
<pre
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import type { ContentKitButton } from '@gitbook/api';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import React from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
@@ -19,16 +20,14 @@ export function ElementButton(
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
// TODO:
|
||||
// - loading
|
||||
// - confirm
|
||||
// - confirm dialog
|
||||
|
||||
return (
|
||||
<button
|
||||
title={element.tooltip}
|
||||
className={classNames(
|
||||
'contentkit-button',
|
||||
`contentkit-button-style-${element.style ?? 'secondary'}`,
|
||||
loading ? 'contentkit-button-loading' : null
|
||||
`contentkit-button-style-${element.style ?? 'secondary'}`
|
||||
)}
|
||||
onClick={(event) => {
|
||||
if (element.disabled || loading) {
|
||||
@@ -43,11 +42,17 @@ export function ElementButton(
|
||||
});
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{element.label ? (
|
||||
<span className="contentkit-button-label">{element.label}</span>
|
||||
) : null}
|
||||
{trailingIcon}
|
||||
{loading ? (
|
||||
<Icon icon="spinner" className="contentkit-button-loading" />
|
||||
) : (
|
||||
<>
|
||||
{icon}
|
||||
{element.label ? (
|
||||
<span className="contentkit-button-label">{element.label}</span>
|
||||
) : null}
|
||||
{trailingIcon}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import type {
|
||||
ContentKitButton,
|
||||
ContentKitCheckbox,
|
||||
ContentKitCodeBlock,
|
||||
ContentKitInput,
|
||||
ContentKitRadio,
|
||||
ContentKitSelect,
|
||||
ContentKitSwitch,
|
||||
ContentKitTextInput,
|
||||
} from '@gitbook/api';
|
||||
import classNames from 'classnames';
|
||||
import { ElementButton } from './ElementButton';
|
||||
import { ElementCodeBlock } from './ElementCodeBlock';
|
||||
import { ElementIcon } from './ElementIcon';
|
||||
import { ElementTextInput } from './ElementTextInput';
|
||||
import type { ContentKitServerContext } from './types';
|
||||
|
||||
export function ElementInput(props: {
|
||||
element: ContentKitInput;
|
||||
context: ContentKitServerContext;
|
||||
state: object;
|
||||
}) {
|
||||
const { element, context, state } = props;
|
||||
const { label, element: childElement } = element;
|
||||
|
||||
return (
|
||||
<div className={classNames('contentkit-form')}>
|
||||
{label ? <label className="contentkit-label">{label}</label> : null}
|
||||
{getInputElement({ element: childElement, context, state })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getInputElement(props: {
|
||||
element:
|
||||
| ContentKitTextInput
|
||||
| ContentKitSelect
|
||||
| ContentKitSwitch
|
||||
| ContentKitRadio
|
||||
| ContentKitCheckbox
|
||||
| ContentKitButton
|
||||
| ContentKitCodeBlock;
|
||||
context: ContentKitServerContext;
|
||||
state: object;
|
||||
}) {
|
||||
const { element, context, state } = props;
|
||||
|
||||
//!!TODO add support for:
|
||||
// - select
|
||||
// - switch
|
||||
// - radio
|
||||
// - checkbox
|
||||
|
||||
switch (element.type) {
|
||||
case 'textinput':
|
||||
return <ElementTextInput element={element} />;
|
||||
case 'button':
|
||||
return (
|
||||
<ElementButton
|
||||
element={element}
|
||||
icon={
|
||||
element.icon ? <ElementIcon icon={element.icon} context={context} /> : null
|
||||
}
|
||||
trailingIcon={
|
||||
element.trailingIcon ? (
|
||||
<ElementIcon icon={element.trailingIcon} context={context} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
);
|
||||
case 'codeblock':
|
||||
return <ElementCodeBlock element={element} context={context} state={state} />;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ export function ElementTextInput(props: ContentKitClientElementProps<ContentKitT
|
||||
value={value}
|
||||
placeholder={element.placeholder}
|
||||
onChange={onChange}
|
||||
rows={4}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user