mirror of
https://github.com/urbit/shrub.git
synced 2024-11-28 22:33:06 +03:00
Merge branch 'lf/indigo-bump' (#3540)
* origin/lf/indigo-bump: publish: set max width on new note title input interface: fix flexbox for groupView button layout interface: add cursor css to tabs publish: use Row for NoteForm layout interface: cleanup more indigo shenanigans remove fontSize property profile: fix form breakage caused by 1.2 migration publish: use CodeMirror as editor Revert "publish: introduce ProseMirror for markdown editing" interface: fix assorted breakage from 1.2 indigo bump publish: introduce ProseMirror for markdown editing interface: bump indigo version Signed-off-by: Matilde Park <matilde@tlon.io>
This commit is contained in:
commit
c3991676c2
23
pkg/interface/package-lock.json
generated
23
pkg/interface/package-lock.json
generated
@ -1709,9 +1709,21 @@
|
||||
"integrity": "sha512-3OPSdf9cejP/TSzWXuBaYbzLtAfBzQnc75SlPLkoPfwpxnv1Bvy9hiWngLY0WnKRR6lMOldnkYQCCuNWeDibYQ=="
|
||||
},
|
||||
"@tlon/indigo-react": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.1.15.tgz",
|
||||
"integrity": "sha512-Ao+1hAJjN5y1gDyT7GIUgXORPXTIpZKVVtrS++ZGYBemYMSq3oJFMIZertsSZbDHuh/TsVPenJrMUZBpV60law=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.6.tgz",
|
||||
"integrity": "sha512-Dng+OfQ6ViMrdJXtQvgsVrW9vglPGUmbv0ffi2MSwLfe6FhUdL1CHoOjGxm4pATLOou53Kqcrt4g1Svw7y9THw==",
|
||||
"requires": {
|
||||
"@reach/menu-button": "^0.10.5",
|
||||
"react": "^16.13.1",
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
|
||||
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/anymatch": {
|
||||
"version": "1.3.1",
|
||||
@ -6895,6 +6907,11 @@
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||
},
|
||||
"normalize-wheel": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
|
||||
"integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU="
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||
|
@ -9,7 +9,7 @@
|
||||
"@reach/menu-button": "^0.10.5",
|
||||
"@reach/tabs": "^0.10.5",
|
||||
"@tlon/indigo-light": "^1.0.3",
|
||||
"@tlon/indigo-react": "^1.1.15",
|
||||
"@tlon/indigo-react": "1.2.6",
|
||||
"aws-sdk": "^2.726.0",
|
||||
"classnames": "^2.2.6",
|
||||
"codemirror": "^5.55.0",
|
||||
|
2
pkg/interface/src/types/util.ts
Normal file
2
pkg/interface/src/types/util.ts
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
export type PropFunc<T extends (...args: any[]) => any> = Parameters<T>[0];
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Spinner } from '../../../components/Spinner';
|
||||
import urbitOb from 'urbit-ob';
|
||||
import { Box, Text, Input, Button } from '@tlon/indigo-react';
|
||||
import { Box, Text, ManagedTextInputField as Input, Button } from '@tlon/indigo-react';
|
||||
import { Formik, Form } from 'formik'
|
||||
import * as Yup from 'yup';
|
||||
|
||||
|
@ -1,13 +1,17 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import { Sigil } from "~/logic/lib/sigil";
|
||||
import * as Yup from "yup";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import { EditElement } from "./edit-element";
|
||||
import { Spinner } from "~/views/components/Spinner";
|
||||
import { uxToHex } from "~/logic/lib/util";
|
||||
import { Col, Input, Box, Text, Row } from "@tlon/indigo-react";
|
||||
import { Formik, Form, FormikHelpers } from "formik";
|
||||
import {
|
||||
ManagedForm as Form,
|
||||
Col,
|
||||
ManagedTextInputField as Input,
|
||||
Box,
|
||||
Text,
|
||||
Row,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Formik, FormikHelpers } from "formik";
|
||||
import { Contact } from "~/types/contact-update";
|
||||
import { AsyncButton } from "~/views/components/AsyncButton";
|
||||
import { ColorInput } from "~/views/components/ColorInput";
|
||||
@ -93,30 +97,34 @@ export function ContactCard(props: ContactCardProps) {
|
||||
initialValues={contact}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Col>
|
||||
<Row
|
||||
borderBottom={1}
|
||||
borderBottomColor="washedGray"
|
||||
pb={3}
|
||||
alignItems="center"
|
||||
>
|
||||
<Sigil size={32} classes="" color={hexColor} ship={us} />
|
||||
<Box ml={2}>
|
||||
<Text fontFamily="mono">{us}</Text>
|
||||
</Box>
|
||||
</Row>
|
||||
<ImageInput mt={3} id="avatar" label="Avatar" s3={props.s3} />
|
||||
<ColorInput id="color" label="Sigil Color" />
|
||||
<Input id="nickname" label="Nickname" />
|
||||
<Input id="email" label="Email" />
|
||||
<Input id="phone" label="Phone" />
|
||||
<Input id="website" label="Website" />
|
||||
<Input id="notes" label="Notes" />
|
||||
<AsyncButton primary loadingText="Updating..." border>
|
||||
Save
|
||||
</AsyncButton>
|
||||
</Col>
|
||||
<Form
|
||||
display="grid"
|
||||
gridAutoRows="auto"
|
||||
gridTemplateColumns="100%"
|
||||
gridRowGap="5"
|
||||
maxWidth="400px"
|
||||
>
|
||||
<Row
|
||||
borderBottom={1}
|
||||
borderBottomColor="washedGray"
|
||||
pb={3}
|
||||
alignItems="center"
|
||||
>
|
||||
<Sigil size={32} classes="" color={hexColor} ship={us} />
|
||||
<Box ml={2}>
|
||||
<Text fontFamily="mono">{us}</Text>
|
||||
</Box>
|
||||
</Row>
|
||||
<ImageInput id="avatar" label="Avatar" s3={props.s3} />
|
||||
<ColorInput id="color" label="Sigil Color" />
|
||||
<Input id="nickname" label="Nickname" />
|
||||
<Input id="email" label="Email" />
|
||||
<Input id="phone" label="Phone" />
|
||||
<Input id="website" label="Website" />
|
||||
<Input id="notes" label="Notes" />
|
||||
<AsyncButton primary loadingText="Updating..." border>
|
||||
Save
|
||||
</AsyncButton>
|
||||
</Form>
|
||||
</Formik>
|
||||
</Box>
|
||||
|
@ -1,9 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Box, InputLabel, Radio, Input } from '@tlon/indigo-react';
|
||||
import React from "react";
|
||||
import {
|
||||
Box,
|
||||
Row,
|
||||
Label,
|
||||
Col,
|
||||
ManagedRadioButtonField as Radio,
|
||||
ManagedTextInputField as Input,
|
||||
} from "@tlon/indigo-react";
|
||||
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { S3State } from '~/types';
|
||||
import { ImageInput } from '~/views/components/ImageInput';
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { S3State } from "~/types";
|
||||
import { ImageInput } from "~/views/components/ImageInput";
|
||||
|
||||
export type BgType = "none" | "url" | "color";
|
||||
|
||||
@ -18,37 +25,33 @@ export function BackgroundPicker({
|
||||
api: GlobalApi;
|
||||
s3: S3State;
|
||||
}) {
|
||||
|
||||
const rowSpace = { my: 0, alignItems: 'center' };
|
||||
const radioProps = { my: 4, mr: 4, name: 'bgType' };
|
||||
return (
|
||||
<Box>
|
||||
<InputLabel>Landscape Background</InputLabel>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box mt={3} mr={7}>
|
||||
<Radio label="Image" id="url" name="bgType" />
|
||||
{bgType === "url" && (
|
||||
<ImageInput
|
||||
api={api}
|
||||
s3={s3}
|
||||
id="bgUrl"
|
||||
name="bgUrl"
|
||||
label="URL"
|
||||
url={bgUrl || ""}
|
||||
/>
|
||||
)}
|
||||
<Radio label="Color" id="color" name="bgType" />
|
||||
{bgType === "color" && (
|
||||
<Input
|
||||
ml={4}
|
||||
type="text"
|
||||
label="Color"
|
||||
id="bgColor"
|
||||
name="bgColor"
|
||||
/>
|
||||
)}
|
||||
<Radio label="None" id="none" name="bgType" />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Col>
|
||||
<Label mb="2">Landscape Background</Label>
|
||||
<Row {...rowSpace}>
|
||||
<Radio {...radioProps} label="Image" id="url" />
|
||||
{bgType === "url" && (
|
||||
<ImageInput
|
||||
ml="3"
|
||||
api={api}
|
||||
s3={s3}
|
||||
id="bgUrl"
|
||||
name="bgUrl"
|
||||
label="URL"
|
||||
url={bgUrl || ""}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
<Row {...rowSpace}>
|
||||
<Radio label="Color" id="color" {...radioProps} />
|
||||
{bgType === "color" && (
|
||||
<Input ml={4} type="text" label="Color" id="bgColor" />
|
||||
)}
|
||||
</Row>
|
||||
<Radio label="None" id="none" {...radioProps} />
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import {
|
||||
Input,
|
||||
ManagedTextInputField as Input,
|
||||
ManagedForm as Form,
|
||||
Box,
|
||||
Button,
|
||||
Col,
|
||||
@ -11,9 +12,9 @@ import {
|
||||
MenuList,
|
||||
MenuItem,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Formik, Form } from "formik";
|
||||
import { Formik } from "formik";
|
||||
|
||||
import GlobalApi from "../../../../api/global";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
|
||||
export function BucketList({
|
||||
buckets,
|
||||
@ -53,49 +54,48 @@ export function BucketList({
|
||||
|
||||
return (
|
||||
<Formik initialValues={{ newBucket: "" }} onSubmit={onSubmit}>
|
||||
<Form>
|
||||
<Col alignItems="start">
|
||||
{_buckets.map((bucket) => (
|
||||
<Box
|
||||
key={bucket}
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
borderRadius={1}
|
||||
border={1}
|
||||
borderColor="washedGray"
|
||||
fontSize={1}
|
||||
pl={2}
|
||||
mb={2}
|
||||
width="100%"
|
||||
>
|
||||
<Text>{bucket}</Text>
|
||||
{bucket === selected && (
|
||||
<Text p={1} color="green">
|
||||
Active
|
||||
</Text>
|
||||
)}
|
||||
{bucket !== selected && (
|
||||
<Menu>
|
||||
<MenuButton sm>Options</MenuButton>
|
||||
<MenuList>
|
||||
<MenuItem onSelect={onSelect(bucket)}>Make Active</MenuItem>
|
||||
<MenuItem onSelect={onDelete(bucket)}>Delete</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
<Input
|
||||
mt={2}
|
||||
type="text"
|
||||
label="New Bucket"
|
||||
id="newBucket"
|
||||
/>
|
||||
<Button border borderColor="washedGrey" type="submit">
|
||||
Add
|
||||
</Button>
|
||||
</Col>
|
||||
<Form
|
||||
display="grid"
|
||||
gridTemplateColumns="100%"
|
||||
gridAutoRows="auto"
|
||||
gridRowGap={2}
|
||||
>
|
||||
{_buckets.map((bucket) => (
|
||||
<Box
|
||||
key={bucket}
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
borderRadius={1}
|
||||
border={1}
|
||||
borderColor="washedGray"
|
||||
fontSize={1}
|
||||
pl={2}
|
||||
mb={2}
|
||||
>
|
||||
<Text>{bucket}</Text>
|
||||
{bucket === selected && (
|
||||
<Text p={2} color="green">
|
||||
Active
|
||||
</Text>
|
||||
)}
|
||||
{bucket !== selected && (
|
||||
<Menu>
|
||||
<MenuButton border={0} cursor="pointer" width="auto">
|
||||
Options
|
||||
</MenuButton>
|
||||
<MenuList>
|
||||
<MenuItem onSelect={onSelect(bucket)}>Make Active</MenuItem>
|
||||
<MenuItem onSelect={onDelete(bucket)}>Delete</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
<Input mt="2" label="New Bucket" id="newBucket" />
|
||||
<Button mt="2" borderColor="washedGrey" type="submit">
|
||||
Add
|
||||
</Button>
|
||||
</Form>
|
||||
</Formik>
|
||||
);
|
||||
|
@ -2,8 +2,8 @@ import React from "react";
|
||||
|
||||
import {
|
||||
Box,
|
||||
InputLabel,
|
||||
Checkbox,
|
||||
Label,
|
||||
ManagedCheckboxField as Checkbox,
|
||||
Button,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Formik, Form } from "formik";
|
||||
@ -14,7 +14,7 @@ import GlobalApi from "../../../../api/global";
|
||||
import { LaunchState } from "../../../../types/launch-update";
|
||||
import { DropLaunchTiles } from "./DropLaunch";
|
||||
import { S3State, BackgroundConfig } from "../../../../types";
|
||||
import { BackgroundPicker, BgType } from './BackgroundPicker';
|
||||
import { BackgroundPicker, BgType } from "./BackgroundPicker";
|
||||
|
||||
const formSchema = Yup.object().shape({
|
||||
tileOrdering: Yup.array().of(Yup.string()),
|
||||
@ -47,14 +47,7 @@ interface DisplayFormProps {
|
||||
}
|
||||
|
||||
export default function DisplayForm(props: DisplayFormProps) {
|
||||
const {
|
||||
api,
|
||||
launch,
|
||||
background,
|
||||
hideAvatars,
|
||||
hideNicknames,
|
||||
s3
|
||||
} = props;
|
||||
const { api, launch, background, hideAvatars, hideNicknames, s3 } = props;
|
||||
|
||||
let bgColor, bgUrl;
|
||||
if (background?.type === "url") {
|
||||
@ -99,17 +92,17 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
<Form>
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateColumns="1fr"
|
||||
gridTemplateColumns="100%"
|
||||
gridTemplateRows="auto"
|
||||
gridRowGap={3}
|
||||
gridRowGap={5}
|
||||
>
|
||||
<Box color="black" fontSize={1} mb={3} fontWeight={900}>
|
||||
Display Preferences
|
||||
</Box>
|
||||
<Box mb={2}>
|
||||
<InputLabel display="block" pb={2}>
|
||||
<Label display="block" pb={2}>
|
||||
Tile Order
|
||||
</InputLabel>
|
||||
</Label>
|
||||
<DropLaunchTiles
|
||||
id="tileOrdering"
|
||||
name="tileOrdering"
|
||||
@ -123,22 +116,20 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
api={api}
|
||||
s3={s3}
|
||||
/>
|
||||
<Box>
|
||||
<Checkbox
|
||||
label="Disable avatars"
|
||||
id="avatars"
|
||||
caption="Do not show user-set avatars"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Disable nicknames"
|
||||
id="nicknames"
|
||||
caption="Do not show user-set nicknames"
|
||||
/>
|
||||
</Box>
|
||||
<Checkbox
|
||||
label="Disable avatars"
|
||||
id="avatars"
|
||||
caption="Do not show user-set avatars"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Disable nicknames"
|
||||
id="nicknames"
|
||||
caption="Do not show user-set nicknames"
|
||||
/>
|
||||
<Button border={1} borderColor="washedGray" type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Box>
|
||||
<Button border={1} borderColor="washedGray" type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
@ -4,11 +4,11 @@ import { usePreview } from "react-dnd-multi-backend";
|
||||
import { capitalize } from "lodash";
|
||||
import { TileTypeBasic, Tile } from "../../../../types/launch-update";
|
||||
|
||||
import { Box, Img as _Img, Text } from "@tlon/indigo-react";
|
||||
import { Box, Image as _Image, Text } from "@tlon/indigo-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
// Need to change dojo image
|
||||
const Img = styled(_Img)<{ invert?: boolean }>`
|
||||
const Image = styled(_Image)<{ invert?: boolean }>`
|
||||
${(p) =>
|
||||
p.theme.colors.white !== "rgba(255,255,255,1)" ? `filter: invert(1);` : ``}
|
||||
|
||||
@ -83,7 +83,7 @@ function DragTileBasic(props: {
|
||||
}
|
||||
style={props.style}
|
||||
>
|
||||
<Img width="48px" height="48px" src={tile.iconUrl} invert={isDojo} />
|
||||
<Image width="48px" height="48px" src={tile.iconUrl} invert={isDojo} />
|
||||
<Text
|
||||
color={
|
||||
"black" // isDojo ? "white" : "black"
|
||||
|
@ -1,5 +1,9 @@
|
||||
import React from "react";
|
||||
import { Box, Button, Checkbox } from '@tlon/indigo-react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ManagedCheckboxField as Checkbox,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Formik, Form } from "formik";
|
||||
import * as Yup from "yup";
|
||||
|
||||
@ -10,7 +14,7 @@ const formSchema = Yup.object().shape({
|
||||
imageShown: Yup.boolean(),
|
||||
audioShown: Yup.boolean(),
|
||||
videoShown: Yup.boolean(),
|
||||
oembedShown: Yup.boolean()
|
||||
oembedShown: Yup.boolean(),
|
||||
});
|
||||
|
||||
interface FormSchema {
|
||||
@ -39,7 +43,7 @@ export default function RemoteContentForm(props: RemoteContentFormProps) {
|
||||
imageShown,
|
||||
audioShown,
|
||||
videoShown,
|
||||
oembedShown
|
||||
oembedShown,
|
||||
} as FormSchema
|
||||
}
|
||||
onSubmit={(values, actions) => {
|
||||
@ -47,7 +51,7 @@ export default function RemoteContentForm(props: RemoteContentFormProps) {
|
||||
imageShown: values.imageShown,
|
||||
audioShown: values.audioShown,
|
||||
videoShown: values.videoShown,
|
||||
oembedShown: values.oembedShown
|
||||
oembedShown: values.oembedShown,
|
||||
});
|
||||
api.local.dehydrate();
|
||||
actions.setSubmitting(false);
|
||||
@ -59,36 +63,26 @@ export default function RemoteContentForm(props: RemoteContentFormProps) {
|
||||
display="grid"
|
||||
gridTemplateColumns="1fr"
|
||||
gridTemplateRows="audio"
|
||||
gridRowGap={3}
|
||||
gridRowGap={5}
|
||||
>
|
||||
<Box color="black" fontSize={1} mb={3} fontWeight={900}>
|
||||
<Box color="black" fontSize={1} fontWeight={900}>
|
||||
Remote Content
|
||||
</Box>
|
||||
<Box>
|
||||
<Checkbox
|
||||
label="Load images"
|
||||
id="imageShown"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Load audio files"
|
||||
id="audioShown"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Load video files"
|
||||
id="videoShown"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Load embedded content"
|
||||
id="oembedShown"
|
||||
caption="Embedded content may contain scripts"
|
||||
/>
|
||||
</Box>
|
||||
<Checkbox label="Load images" id="imageShown" />
|
||||
<Checkbox label="Load audio files" id="audioShown" />
|
||||
<Checkbox label="Load video files" id="videoShown" />
|
||||
<Checkbox
|
||||
label="Load embedded content"
|
||||
id="oembedShown"
|
||||
caption="Embedded content may contain scripts"
|
||||
/>
|
||||
<Button border={1} borderColor="washedGray" type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Box>
|
||||
<Button border={1} borderColor="washedGray" type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,18 @@
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import {
|
||||
Input,
|
||||
ManagedTextInputField as Input,
|
||||
ManagedForm as Form,
|
||||
Box,
|
||||
Button,
|
||||
Col,
|
||||
Text,
|
||||
Menu
|
||||
Menu,
|
||||
} from "@tlon/indigo-react";
|
||||
|
||||
import { Formik, Form } from "formik";
|
||||
import { Formik } from "formik";
|
||||
import GlobalApi from "../../../../api/global";
|
||||
import { BucketList } from './BucketList';
|
||||
import { BucketList } from "./BucketList";
|
||||
import { S3State } from "../../../../types";
|
||||
|
||||
interface FormSchema {
|
||||
@ -49,9 +50,6 @@ export default function S3Form(props: S3FormProps) {
|
||||
return (
|
||||
<>
|
||||
<Col>
|
||||
<Box color="black" mb={4} fontSize={1} fontWeight={900}>
|
||||
S3 Credentials
|
||||
</Box>
|
||||
<Formik
|
||||
initialValues={
|
||||
{
|
||||
@ -64,23 +62,23 @@ export default function S3Form(props: S3FormProps) {
|
||||
}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Input width="256px" type="text" label="Endpoint" id="s3endpoint" />
|
||||
<Form
|
||||
display="grid"
|
||||
gridTemplateColumns="100%"
|
||||
gridAutoRows="auto"
|
||||
gridRowGap={5}
|
||||
>
|
||||
<Box color="black" fontSize={1} fontWeight={900}>
|
||||
S3 Credentials
|
||||
</Box>
|
||||
<Input label="Endpoint" id="s3endpoint" />
|
||||
<Input label="Access Key ID" id="s3accessKeyId" />
|
||||
<Input
|
||||
width="256px"
|
||||
type="text"
|
||||
label="Access Key ID"
|
||||
id="s3accessKeyId"
|
||||
/>
|
||||
<Input
|
||||
width="256px"
|
||||
type="password"
|
||||
label="Secret Access Key"
|
||||
id="s3secretAccessKey"
|
||||
/>
|
||||
<Button border={1} type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button type="submit">Submit</Button>
|
||||
</Form>
|
||||
</Formik>
|
||||
</Col>
|
||||
|
@ -36,7 +36,6 @@ export default function Settings({
|
||||
return (
|
||||
<Box
|
||||
backgroundColor="white"
|
||||
fontSize={2}
|
||||
display="grid"
|
||||
gridTemplateRows="auto"
|
||||
gridTemplateColumns="1fr"
|
||||
|
@ -22,7 +22,7 @@ const SidebarItem = ({ children, view, current }) => {
|
||||
px={3}
|
||||
backgroundColor={selected ? "washedBlue" : "white"}
|
||||
>
|
||||
<Icon mr={2} display="inline-block" icon="Circle" fill={color} />
|
||||
<Icon mr={2} display="inline-block" icon="Circle" color={color} />
|
||||
<Text color={color} fontSize={0}>
|
||||
{children}
|
||||
</Text>
|
||||
|
@ -2,7 +2,7 @@ import React from "react";
|
||||
import * as Yup from "yup";
|
||||
import { Formik, FormikHelpers, Form, useFormikContext } from "formik";
|
||||
import { AsyncButton } from "../../../../components/AsyncButton";
|
||||
import { TextArea } from "@tlon/indigo-react";
|
||||
import { ManagedTextAreaField as TextArea } from "@tlon/indigo-react";
|
||||
|
||||
interface FormSchema {
|
||||
comment: string;
|
||||
@ -48,7 +48,7 @@ export default function CommentInput(props: CommentInputProps) {
|
||||
id="comment"
|
||||
placeholder={props.placeholder || ""}
|
||||
/>
|
||||
<AsyncButton loadingText={loading} border type="submit">
|
||||
<AsyncButton mt={2} loadingText={loading} border type="submit">
|
||||
{label}
|
||||
</AsyncButton>
|
||||
</Form>
|
||||
|
@ -38,7 +38,7 @@ export function EditPost(props: EditPostProps & RouteComponentProps) {
|
||||
<PostForm
|
||||
initial={initial}
|
||||
onSubmit={onSubmit}
|
||||
submitLabel={`Update ${note.title}`}
|
||||
submitLabel="Update"
|
||||
loadingText="Updating..."
|
||||
/>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState, useRef, useEffect } from "react";
|
||||
import { Col, Text, ErrorMessage } from "@tlon/indigo-react";
|
||||
import { Col, Text, ErrorLabel } from "@tlon/indigo-react";
|
||||
import { Spinner } from "~/views/components/Spinner";
|
||||
import { Notebooks } from "~/types/publish-update";
|
||||
import { useWaitForProps } from "~/logic/lib/useWaitForProps";
|
||||
@ -46,7 +46,7 @@ export function JoinScreen(props: JoinScreenProps & RouteComponentProps) {
|
||||
<Col p={4}>
|
||||
<Text fontSize={1}>Joining Notebook</Text>
|
||||
<Spinner awaiting text="Joining..." />
|
||||
{error && <ErrorMessage>Unable to join notebook</ErrorMessage>}
|
||||
{error && <ErrorLabel>Unable to join notebook</ErrorLabel>}
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import { UnControlled as CodeEditor } from "react-codemirror2";
|
||||
import { MOBILE_BROWSER_REGEX } from "~/logic/lib/util";
|
||||
import { PropFunc } from "~/types/util";
|
||||
import CodeMirror from "codemirror";
|
||||
|
||||
import "codemirror/mode/markdown/markdown";
|
||||
import "codemirror/addon/display/placeholder";
|
||||
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import { Box } from "@tlon/indigo-react";
|
||||
|
||||
const MARKDOWN_CONFIG = {
|
||||
name: "markdown",
|
||||
};
|
||||
|
||||
interface MarkdownEditorProps {
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
onChange: (s: string) => void;
|
||||
onBlur?: (e: any) => void;
|
||||
}
|
||||
|
||||
export function MarkdownEditor(
|
||||
props: MarkdownEditorProps & PropFunc<typeof Box>
|
||||
) {
|
||||
const { onBlur, placeholder, value, onChange, ...boxProps } = props;
|
||||
|
||||
const options = {
|
||||
mode: MARKDOWN_CONFIG,
|
||||
theme: "tlon",
|
||||
lineNumbers: false,
|
||||
lineWrapping: true,
|
||||
scrollbarStyle: "native",
|
||||
// cursorHeight: 0.85,
|
||||
placeholder: placeholder || "",
|
||||
};
|
||||
|
||||
const handleChange = useCallback(
|
||||
(_e, _d, v: string) => {
|
||||
onChange(v);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const handleBlur = useCallback(
|
||||
(_i, e: any) => {
|
||||
onBlur && onBlur(e);
|
||||
},
|
||||
[onBlur]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
flexGrow={1}
|
||||
position="static"
|
||||
className="publish"
|
||||
p={1}
|
||||
border={1}
|
||||
borderColor="lightGray"
|
||||
borderRadius={2}
|
||||
{...boxProps}
|
||||
>
|
||||
<CodeEditor
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -1,27 +1,43 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { MarkdownEditor as _MarkdownEditor, Box, ErrorMessage } from '@tlon/indigo-react';
|
||||
import { useField } from 'formik';
|
||||
import React, { useCallback } from "react";
|
||||
import _ from "lodash";
|
||||
import { Box, ErrorLabel } from "@tlon/indigo-react";
|
||||
import { useField } from "formik";
|
||||
import { MarkdownEditor } from "./MarkdownEditor";
|
||||
|
||||
const MarkdownEditor = styled(_MarkdownEditor)`
|
||||
border: 1px solid ${(p) => p.theme.colors.lightGray};
|
||||
border-radius: ${(p) => p.theme.radii[2]}px;
|
||||
`;
|
||||
export const MarkdownField = ({
|
||||
id,
|
||||
...rest
|
||||
}: { id: string } & Parameters<typeof Box>[0]) => {
|
||||
const [{ value, onBlur }, { error, touched }, { setValue }] = useField(id);
|
||||
|
||||
export const MarkdownField = ({ id, ...rest }: { id: string; } & Parameters<typeof Box>[0]) => {
|
||||
const [{ value }, { error, touched }, { setValue, setTouched }] = useField(id);
|
||||
const handleBlur = useCallback(
|
||||
(e: any) => {
|
||||
_.set(e, "target.id", id);
|
||||
console.log(e);
|
||||
onBlur && onBlur(e);
|
||||
},
|
||||
[onBlur, id]
|
||||
);
|
||||
|
||||
const hasError = !!(error && touched);
|
||||
|
||||
return (
|
||||
<Box overflowY="hidden" width="100%" display="flex" flexDirection="column" {...rest}>
|
||||
<Box
|
||||
overflowY="hidden"
|
||||
width="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
{...rest}
|
||||
>
|
||||
<MarkdownEditor
|
||||
onFocus={() => setTouched(true)}
|
||||
onBlur={() => setTouched(false)}
|
||||
borderColor={hasError ? "red" : "lightGray"}
|
||||
onBlur={handleBlur}
|
||||
value={value}
|
||||
onBeforeChange={(e, d, v) => setValue(v)}
|
||||
onChange={setValue}
|
||||
/>
|
||||
<ErrorMessage>{touched && error}</ErrorMessage>
|
||||
<ErrorLabel mt="2" hasError={!!(error && touched)}>
|
||||
{error}
|
||||
</ErrorLabel>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
@ -3,11 +3,9 @@ import { AsyncButton } from "../../../../components/AsyncButton";
|
||||
import * as Yup from "yup";
|
||||
import {
|
||||
Box,
|
||||
Input,
|
||||
Checkbox,
|
||||
ManagedTextInputField as Input,
|
||||
ManagedCheckboxField as Checkbox,
|
||||
Col,
|
||||
InputLabel,
|
||||
InputCaption,
|
||||
Button,
|
||||
Center,
|
||||
} from "@tlon/indigo-react";
|
||||
|
@ -1,6 +1,11 @@
|
||||
import React from "react";
|
||||
import * as Yup from "yup";
|
||||
import { Box, Input } from "@tlon/indigo-react";
|
||||
import {
|
||||
Box,
|
||||
ManagedTextInputField as Input,
|
||||
Row,
|
||||
Col,
|
||||
} from "@tlon/indigo-react";
|
||||
import { AsyncButton } from "../../../../components/AsyncButton";
|
||||
import { Formik, Form, FormikHelpers } from "formik";
|
||||
import { MarkdownField } from "./MarkdownField";
|
||||
@ -29,32 +34,29 @@ export function PostForm(props: PostFormProps) {
|
||||
const { initial, onSubmit, submitLabel, loadingText } = props;
|
||||
|
||||
return (
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
p={[2, 4]}
|
||||
display="grid"
|
||||
justifyItems="start"
|
||||
gridTemplateRows={["64px 64px 1fr", "64px 1fr"]}
|
||||
gridTemplateColumns={["100%", "1fr 1fr"]}
|
||||
gridColumnGap={2}
|
||||
gridRowGap={2}
|
||||
>
|
||||
<Col width="100%" height="100%" p={[2, 4]}>
|
||||
<Formik
|
||||
validationSchema={formSchema}
|
||||
initialValues={initial}
|
||||
onSubmit={onSubmit}
|
||||
validateOnBlur
|
||||
>
|
||||
<Form style={{ display: "contents" }}>
|
||||
<Input width="100%" placeholder="Post Title" id="title" />
|
||||
<Box gridRow={["1/2", "auto"]} mt={1} justifySelf={["start", "end"]}>
|
||||
<AsyncButton primary loadingText={loadingText}>
|
||||
<Row flexDirection={["column-reverse", "row"]} mb={4} gapX={4} justifyContent='space-between'>
|
||||
<Input maxWidth='40rem' flexGrow={1} placeholder="Post Title" id="title" />
|
||||
<AsyncButton
|
||||
ml={[0,2]}
|
||||
mb={[4,0]}
|
||||
flexShrink={1}
|
||||
primary
|
||||
loadingText={loadingText}
|
||||
>
|
||||
{submitLabel}
|
||||
</AsyncButton>
|
||||
</Box>
|
||||
<MarkdownField gridColumn={["1/2", "1/3"]} id="body" />
|
||||
</Row>
|
||||
<MarkdownField flexGrow={1} id="body" />
|
||||
</Form>
|
||||
</Formik>
|
||||
</Box>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
@ -3,35 +3,18 @@ import { Link, RouteComponentProps, Route, Switch } from "react-router-dom";
|
||||
import { NotebookPosts } from "./NotebookPosts";
|
||||
import { Subscribers } from "./Subscribers";
|
||||
import { Settings } from "./Settings";
|
||||
import { Spinner } from '~/views/components/Spinner';
|
||||
import { Spinner } from "~/views/components/Spinner";
|
||||
import { Tabs, Tab } from "~/views/components/Tab";
|
||||
import { roleForShip } from "~/logic/lib/group";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Text,
|
||||
Tab as _Tab,
|
||||
Tabs,
|
||||
TabList as _TabList,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
Row,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Box, Button, Text, Row } from "@tlon/indigo-react";
|
||||
import { Notebook as INotebook } from "~/types/publish-update";
|
||||
import { Groups } from "~/types/group-update";
|
||||
import { Contacts, Rolodex } from "~/types/contact-update";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import styled from "styled-components";
|
||||
import {Associations} from "~/types";
|
||||
import { Associations } from "~/types";
|
||||
import { deSig } from "~/logic/lib/util";
|
||||
|
||||
const TabList = styled(_TabList)`
|
||||
margin-bottom: ${(p) => p.theme.space[4]}px;
|
||||
`;
|
||||
|
||||
const Tab = styled(_Tab)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
interface NotebookProps {
|
||||
api: GlobalApi;
|
||||
ship: string;
|
||||
@ -46,18 +29,39 @@ interface NotebookProps {
|
||||
|
||||
interface NotebookState {
|
||||
isUnsubscribing: boolean;
|
||||
tab: string;
|
||||
}
|
||||
|
||||
export class Notebook extends PureComponent<NotebookProps & RouteComponentProps, NotebookState> {
|
||||
export class Notebook extends PureComponent<
|
||||
NotebookProps & RouteComponentProps,
|
||||
NotebookState
|
||||
> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isUnsubscribing: false
|
||||
isUnsubscribing: false,
|
||||
tab: "all",
|
||||
};
|
||||
this.setTab = this.setTab.bind(this);
|
||||
}
|
||||
|
||||
setTab(tab: string) {
|
||||
this.setState({ tab });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { api, ship, book, notebook, notebookContacts, groups, history, hideNicknames, associations } = this.props;
|
||||
const {
|
||||
api,
|
||||
ship,
|
||||
book,
|
||||
notebook,
|
||||
notebookContacts,
|
||||
groups,
|
||||
history,
|
||||
hideNicknames,
|
||||
associations,
|
||||
} = this.props;
|
||||
const { state } = this;
|
||||
|
||||
const group = groups[notebook?.["writers-group-path"]];
|
||||
if (!group) return null; // Waitin on groups to populate
|
||||
@ -66,14 +70,14 @@ export class Notebook extends PureComponent<NotebookProps & RouteComponentProps,
|
||||
const role = group ? roleForShip(group, window.ship) : undefined;
|
||||
const isOwn = `~${window.ship}` === ship;
|
||||
const isAdmin = role === "admin" || isOwn;
|
||||
|
||||
|
||||
const isWriter =
|
||||
isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship);
|
||||
|
||||
|
||||
const notesList = notebook?.["notes-by-date"] || [];
|
||||
const notes = notebook?.notes || {};
|
||||
const showNickname = contact?.nickname && !hideNicknames;
|
||||
|
||||
|
||||
return (
|
||||
<Box
|
||||
pt={4}
|
||||
@ -99,71 +103,103 @@ export class Notebook extends PureComponent<NotebookProps & RouteComponentProps,
|
||||
<Row justifyContent={["flex-start", "flex-end"]}>
|
||||
{isWriter && (
|
||||
<Link to={`/~publish/notebook/${ship}/${book}/new`}>
|
||||
<Button primary border>
|
||||
New Post
|
||||
</Button>
|
||||
<Button primary>New Post</Button>
|
||||
</Link>
|
||||
)}
|
||||
{!isOwn
|
||||
? this.state.isUnsubscribing
|
||||
? <Spinner awaiting={this.state.isUnsubscribing} classes="mt2 ml2" text="Unsubscribing..." />
|
||||
: <Button ml={isWriter ? 2 : 0} error border onClick={() => {
|
||||
this.setState({ isUnsubscribing: true });
|
||||
api.publish.unsubscribeNotebook(deSig(ship), book).then(() => {
|
||||
history.push("/~publish");
|
||||
}).catch(() => {
|
||||
this.setState({ isUnsubscribing: false });
|
||||
});
|
||||
}}>
|
||||
{!isOwn ? (
|
||||
this.state.isUnsubscribing ? (
|
||||
<Spinner
|
||||
awaiting={this.state.isUnsubscribing}
|
||||
classes="mt2 ml2"
|
||||
text="Unsubscribing..."
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
ml={isWriter ? 2 : 0}
|
||||
destructive
|
||||
onClick={() => {
|
||||
this.setState({ isUnsubscribing: true });
|
||||
api.publish
|
||||
.unsubscribeNotebook(deSig(ship), book)
|
||||
.then(() => {
|
||||
history.push("/~publish");
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState({ isUnsubscribing: false });
|
||||
});
|
||||
}}
|
||||
>
|
||||
Unsubscribe
|
||||
</Button>
|
||||
: null
|
||||
}
|
||||
)
|
||||
) : null}
|
||||
</Row>
|
||||
<Box gridColumn={["1/2", "1/3"]}>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab>All Posts</Tab>
|
||||
<Tab>About</Tab>
|
||||
{isAdmin && <Tab>Subscribers</Tab>}
|
||||
{isOwn && <Tab>Settings</Tab>}
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<NotebookPosts
|
||||
notes={notes}
|
||||
list={notesList}
|
||||
host={ship}
|
||||
book={book}
|
||||
contacts={notebookContacts}
|
||||
hideNicknames={hideNicknames}
|
||||
<Tab
|
||||
selected={state.tab}
|
||||
setSelected={this.setTab}
|
||||
label="All Posts"
|
||||
id="all"
|
||||
/>
|
||||
<Tab
|
||||
selected={state.tab}
|
||||
setSelected={this.setTab}
|
||||
label="About"
|
||||
id="about"
|
||||
/>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<Tab
|
||||
selected={state.tab}
|
||||
setSelected={this.setTab}
|
||||
label="Subscribers"
|
||||
id="subscribers"
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Box color="black">{notebook?.about}</Box>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Subscribers
|
||||
host={ship}
|
||||
book={book}
|
||||
notebook={notebook}
|
||||
api={api}
|
||||
groups={groups}
|
||||
<Tab
|
||||
selected={state.tab}
|
||||
setSelected={this.setTab}
|
||||
label="Settings"
|
||||
id="settings"
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Settings
|
||||
host={ship}
|
||||
book={book}
|
||||
api={api}
|
||||
notebook={notebook}
|
||||
contacts={notebookContacts}
|
||||
associations={associations}
|
||||
groups={groups}
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</>
|
||||
)}
|
||||
</Tabs>
|
||||
{state.tab === "all" && (
|
||||
<NotebookPosts
|
||||
notes={notes}
|
||||
list={notesList}
|
||||
host={ship}
|
||||
book={book}
|
||||
contacts={notebookContacts}
|
||||
hideNicknames={hideNicknames}
|
||||
/>
|
||||
)}
|
||||
{state.tab === "about" && (
|
||||
<Box mt="3" color="black">
|
||||
{notebook?.about}
|
||||
</Box>
|
||||
)}
|
||||
{state.tab === "subscribers" && (
|
||||
<Subscribers
|
||||
host={ship}
|
||||
book={book}
|
||||
notebook={notebook}
|
||||
api={api}
|
||||
groups={groups}
|
||||
/>
|
||||
)}
|
||||
{state.tab === "settings" && (
|
||||
<Settings
|
||||
host={ship}
|
||||
book={book}
|
||||
api={api}
|
||||
notebook={notebook}
|
||||
contacts={notebookContacts}
|
||||
associations={associations}
|
||||
groups={groups}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -15,7 +15,7 @@ interface NotebookPostsProps {
|
||||
|
||||
export function NotebookPosts(props: NotebookPostsProps) {
|
||||
return (
|
||||
<Col>
|
||||
<Col mt="3">
|
||||
{props.list.map((noteId: NoteId) => {
|
||||
const note = props.notes[noteId];
|
||||
if (!note) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Box, Col, Button, InputLabel, InputCaption } from "@tlon/indigo-react";
|
||||
import { Box, Col, Button, Label } from "@tlon/indigo-react";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { Notebook } from "~/types/publish-update";
|
||||
import { Contacts } from "~/types/contact-update";
|
||||
@ -20,7 +20,7 @@ interface SettingsProps {
|
||||
}
|
||||
|
||||
const Divider = (props) => (
|
||||
<Box {...props} mb={4} borderBottom={1} borderBottomColor="lightGray" />
|
||||
<Box {...props} borderBottom={1} borderBottomColor="lightGray" />
|
||||
);
|
||||
export function Settings(props: SettingsProps) {
|
||||
const history = useHistory();
|
||||
@ -36,10 +36,11 @@ export function Settings(props: SettingsProps) {
|
||||
<Box
|
||||
mx="auto"
|
||||
maxWidth="300px"
|
||||
mb={4}
|
||||
my={4}
|
||||
gridTemplateColumns="1fr"
|
||||
gridAutoRows="auto"
|
||||
display="grid"
|
||||
gridRowGap={5}
|
||||
>
|
||||
{isUnmanaged && (
|
||||
<>
|
||||
@ -50,12 +51,12 @@ export function Settings(props: SettingsProps) {
|
||||
<MetadataForm {...props} />
|
||||
<Divider />
|
||||
<Col mb={4}>
|
||||
<InputLabel>Delete Notebook</InputLabel>
|
||||
<InputCaption>
|
||||
<Label>Delete Notebook</Label>
|
||||
<Label gray mt="2">
|
||||
Permanently delete this notebook. (All current members will no longer
|
||||
see this notebook.)
|
||||
</InputCaption>
|
||||
<Button onClick={onDelete} mt={1} border error>
|
||||
</Label>
|
||||
<Button mt="2" onClick={onDelete} destructive>
|
||||
Delete this notebook
|
||||
</Button>
|
||||
</Col>
|
||||
|
@ -106,7 +106,8 @@ export function Sidebar(props: any) {
|
||||
pt={[3, 0]}
|
||||
overflowY="auto"
|
||||
display={display}
|
||||
maxWidth={["none", "250px"]}
|
||||
flexShrink={0}
|
||||
width={["auto", "250px"]}
|
||||
>
|
||||
<Box>
|
||||
<Link to="/~publish/new" className="green2 pa4 f9 dib">
|
||||
|
@ -80,7 +80,7 @@ export class Subscribers extends Component<SubscribersProps> {
|
||||
const role = roleForShip(group, window.ship)
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mt="3">
|
||||
{ role === 'admin' && (
|
||||
<Button mb={3} border onClick={this.addAll}>
|
||||
Add all members as writers
|
||||
|
@ -58,7 +58,7 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) {
|
||||
<PostForm
|
||||
initial={initialValues}
|
||||
onSubmit={onSubmit}
|
||||
submitLabel={`Publish to ${notebook?.title}`}
|
||||
submitLabel="Publish"
|
||||
loadingText="Posting..."
|
||||
/>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { Box, Input, Col } from "@tlon/indigo-react";
|
||||
import { Box, ManagedTextInputField as Input, Col } from "@tlon/indigo-react";
|
||||
import { Formik, Form } from "formik";
|
||||
import * as Yup from "yup";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
|
@ -65,12 +65,13 @@
|
||||
|
||||
.publish .react-codemirror2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.publish .CodeMirror {
|
||||
padding: 12px;
|
||||
height: 100% !important;
|
||||
max-width: 700px;
|
||||
overflow-y: hidden;
|
||||
width: 100% !important;
|
||||
cursor: text;
|
||||
font-size: 12px;
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React, { ReactNode, useState, useEffect } from "react";
|
||||
|
||||
import { Button } from "@tlon/indigo-react";
|
||||
import { Button, LoadingSpinner } from "@tlon/indigo-react";
|
||||
|
||||
import { Spinner } from "./Spinner";
|
||||
import { useFormikContext } from "formik";
|
||||
|
||||
interface AsyncButtonProps {
|
||||
@ -37,7 +36,12 @@ export function AsyncButton({
|
||||
return (
|
||||
<Button border disabled={!isValid} type="submit" {...rest}>
|
||||
{isSubmitting ? (
|
||||
<Spinner awaiting text={loadingText} />
|
||||
<LoadingSpinner
|
||||
foreground={rest.primary ? "white" : 'black'}
|
||||
background="transparent"
|
||||
awaiting
|
||||
text={loadingText}
|
||||
/>
|
||||
) : success === true ? (
|
||||
"Done"
|
||||
) : success === false ? (
|
||||
|
@ -1,41 +1,34 @@
|
||||
import React from "react";
|
||||
import { useField } from "formik";
|
||||
import styled from "styled-components";
|
||||
import { Col, InputLabel, Row, Box, ErrorMessage } from "@tlon/indigo-react";
|
||||
import {
|
||||
Col,
|
||||
Label,
|
||||
Row,
|
||||
Box,
|
||||
ErrorLabel,
|
||||
StatelessTextInput as Input,
|
||||
} from "@tlon/indigo-react";
|
||||
|
||||
import { uxToHex, hexToUx } from "~/logic/lib/util";
|
||||
|
||||
const Input = styled.input`
|
||||
background-color: ${ p => p.theme.colors.white };
|
||||
color: ${ p => p.theme.colors.black };
|
||||
box-sizing: border-box;
|
||||
border: 1px solid;
|
||||
border-right: none;
|
||||
border-color: ${(p) => p.theme.colors.lightGray};
|
||||
border-top-left-radius: ${(p) => p.theme.radii[2]}px;
|
||||
border-bottom-left-radius: ${(p) => p.theme.radii[2]}px;
|
||||
padding: ${(p) => p.theme.space[2]}px;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
`;
|
||||
|
||||
type ColorInputProps = Parameters<typeof Col>[0] & {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
};
|
||||
|
||||
export function ColorInput(props: ColorInputProps) {
|
||||
const { id, label, ...rest } = props;
|
||||
const [{ value }, { error }, { setValue }] = useField(id);
|
||||
const { id, label, caption, ...rest } = props;
|
||||
const [{ value, onBlur }, meta, { setValue }] = useField(id);
|
||||
|
||||
const hex = value.substr(2).replace('.', '');
|
||||
const padded = hex.padStart(6, '0');
|
||||
const hex = value.substr(2).replace(".", "");
|
||||
const padded = hex.padStart(6, "0");
|
||||
|
||||
const onChange = (e: any) => {
|
||||
const { value: newValue } = e.target as HTMLInputElement;
|
||||
const valid = newValue.match(/^(\d|[a-f]|[A-F]){0,6}$/);
|
||||
|
||||
if(!valid) {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
const result = hexToUx(newValue);
|
||||
@ -43,10 +36,21 @@ export function ColorInput(props: ColorInputProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Col {...rest}>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
<Row mt={2}>
|
||||
<Input onChange={onChange} value={hex} />
|
||||
<Box display="flex" flexDirection="column" {...props}>
|
||||
<Label htmlFor={id}>{label}</Label>
|
||||
{caption ? (
|
||||
<Label mt="2" gray>
|
||||
{caption}
|
||||
</Label>
|
||||
) : null}
|
||||
<Row mt="2" alignItems="flex-end">
|
||||
<Input
|
||||
borderTopRightRadius={0}
|
||||
borderBottomRightRadius={0}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
value={hex}
|
||||
/>
|
||||
<Box
|
||||
borderBottomRightRadius={1}
|
||||
borderTopRightRadius={1}
|
||||
@ -58,7 +62,9 @@ export function ColorInput(props: ColorInputProps) {
|
||||
bg={`#${padded}`}
|
||||
/>
|
||||
</Row>
|
||||
<ErrorMessage mt="2">{error}</ErrorMessage>
|
||||
</Col>
|
||||
<ErrorLabel mt="2" hasError={!!(meta.touched && meta.error)}>
|
||||
{meta.error}
|
||||
</ErrorLabel>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -10,9 +10,8 @@ import _ from "lodash";
|
||||
import Mousetrap from "mousetrap";
|
||||
import {
|
||||
Box,
|
||||
InputLabel,
|
||||
ErrorMessage,
|
||||
InputCaption,
|
||||
Label,
|
||||
ErrorLabel,
|
||||
} from "@tlon/indigo-react";
|
||||
import { useDropdown } from "~/logic/lib/useDropdown";
|
||||
import styled from "styled-components";
|
||||
@ -130,8 +129,8 @@ export function DropdownSearch<C>(props: DropdownSearchProps<C>) {
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
<InputLabel htmlFor={props.id}>{props.label}</InputLabel>
|
||||
{caption ? <InputCaption>{caption}</InputCaption> : null}
|
||||
<Label htmlFor={props.id}>{props.label}</Label>
|
||||
{caption ? <Label mt="2" gray>{caption}</Label> : null}
|
||||
{!props.disabled && (
|
||||
<TextArea
|
||||
ref={textarea}
|
||||
@ -165,7 +164,7 @@ export function DropdownSearch<C>(props: DropdownSearchProps<C>) {
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
<ErrorMessage>{props.error}</ErrorMessage>
|
||||
<ErrorLabel>{props.error}</ErrorLabel>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { useFormikContext } from "formik";
|
||||
import { ErrorMessage } from "@tlon/indigo-react";
|
||||
import { ErrorLabel } from "@tlon/indigo-react";
|
||||
|
||||
export function FormError(props: { message: string }) {
|
||||
const { status } = useFormikContext();
|
||||
@ -8,6 +8,6 @@ export function FormError(props: { message: string }) {
|
||||
let s = status || {};
|
||||
|
||||
return (
|
||||
<ErrorMessage>{"error" in s ? props.message : null}</ErrorMessage>
|
||||
<ErrorLabel>{"error" in s ? props.message : null}</ErrorLabel>
|
||||
);
|
||||
}
|
||||
|
@ -25,16 +25,16 @@ class GroupMember extends Component<{ ship: Patp; options: any[] }, {}> {
|
||||
|
||||
return (
|
||||
<div className='flex justify-between f9 items-center'>
|
||||
<div className='flex flex-column'>
|
||||
<div className='flex flex-column flex-shrink-0'>
|
||||
<Text mono mr='2'>{`${cite(ship)}`}</Text>
|
||||
{children}
|
||||
</div>
|
||||
{options.length > 0 && (
|
||||
<Menu>
|
||||
<MenuButton sm>Options</MenuButton>
|
||||
<MenuButton width='min-content'>Options</MenuButton>
|
||||
<MenuList>
|
||||
{options.map(({ onSelect, text }) => (
|
||||
<MenuItem onSelect={onSelect}>{text}</MenuItem>
|
||||
<MenuItem onSelect={onSelect}><Text fontsize='0'>{text}</Text></MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
|
@ -1,7 +1,13 @@
|
||||
import React, { useRef, useCallback, useState } from "react";
|
||||
|
||||
import { Box, Input, Img, Button } from "@tlon/indigo-react";
|
||||
import GlobalApi from "~/api/global";
|
||||
import {
|
||||
Box,
|
||||
StatelessTextInput as Input,
|
||||
Row,
|
||||
Button,
|
||||
Label,
|
||||
ErrorLabel,
|
||||
} from "@tlon/indigo-react";
|
||||
import { useField } from "formik";
|
||||
import { S3State } from "~/types/s3-update";
|
||||
import { useS3 } from "~/logic/lib/useS3";
|
||||
@ -10,16 +16,17 @@ type ImageInputProps = Parameters<typeof Box>[0] & {
|
||||
id: string;
|
||||
label: string;
|
||||
s3: S3State;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export function ImageInput(props: ImageInputProps) {
|
||||
const { id, label, s3, ...rest } = props;
|
||||
const { id, label, s3, caption, placeholder, ...rest } = props;
|
||||
|
||||
const { uploadDefault, canUpload } = useS3(s3);
|
||||
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const [, , { setValue, setError }] = useField(id);
|
||||
const [field, meta, { setValue, setError }] = useField(id);
|
||||
|
||||
const ref = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
@ -44,29 +51,44 @@ export function ImageInput(props: ImageInputProps) {
|
||||
}, [ref]);
|
||||
|
||||
return (
|
||||
<Box {...rest} display="flex">
|
||||
<Input disabled={uploading} type="text" label={label} id={id} />
|
||||
{canUpload && (
|
||||
<>
|
||||
<Button
|
||||
ml={1}
|
||||
border={3}
|
||||
borderColor="washedGray"
|
||||
style={{ marginTop: "18px" }}
|
||||
onClick={onClick}
|
||||
>
|
||||
{uploading ? "Uploading" : "Upload"}
|
||||
</Button>
|
||||
<input
|
||||
style={{ display: "none" }}
|
||||
type="file"
|
||||
id="fileElement"
|
||||
ref={ref}
|
||||
accept="image/*"
|
||||
onChange={onImageUpload}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Box display="flex" flexDirection="column" {...props}>
|
||||
<Label htmlFor={id}>{label}</Label>
|
||||
{caption ? (
|
||||
<Label mt="2" gray>
|
||||
{caption}
|
||||
</Label>
|
||||
) : null}
|
||||
<Row mt="2" alignItems="flex-end">
|
||||
<Input
|
||||
type={"text"}
|
||||
hasError={meta.touched && meta.error !== undefined}
|
||||
placeholder={placeholder}
|
||||
{...field}
|
||||
/>
|
||||
{canUpload && (
|
||||
<>
|
||||
<Button
|
||||
ml={1}
|
||||
border={1}
|
||||
borderColor="lightGray"
|
||||
onClick={onClick}
|
||||
>
|
||||
{uploading ? "Uploading" : "Upload"}
|
||||
</Button>
|
||||
<input
|
||||
style={{ display: "none" }}
|
||||
type="file"
|
||||
id="fileElement"
|
||||
ref={ref}
|
||||
accept="image/*"
|
||||
onChange={onImageUpload}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
<ErrorLabel mt="2" hasError={!!(meta.touched && meta.error)}>
|
||||
{meta.error}
|
||||
</ErrorLabel>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
32
pkg/interface/src/views/components/Tab.tsx
Normal file
32
pkg/interface/src/views/components/Tab.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import { Box, Text, Row } from "@tlon/indigo-react";
|
||||
|
||||
export const Tab = ({ selected, id, label, setSelected }) => (
|
||||
<Box
|
||||
py={2}
|
||||
borderBottom={1}
|
||||
borderBottomColor={selected === id ? "black" : "washedGray"}
|
||||
px={2}
|
||||
cursor='pointer'
|
||||
flexGrow={1}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
onClick={() => setSelected(id)}
|
||||
>
|
||||
<Text color={selected === id ? "black" : "gray"}>{label}</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Tabs = ({ children, ...rest }: Parameters<typeof Row>[0]) => (
|
||||
<Row
|
||||
bg="white"
|
||||
mb={2}
|
||||
justifyContent="stretch"
|
||||
alignItems="flex-end"
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Row>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user