Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/web/content/data/docs/configuration/fields/color.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"id": "28kmDpbLx4KS1dyZlDsYoeyJNwn",
"type": "Doc",
"root": "data",
"index": "a3l",
"title": "Color",
"blocks": [
{
"id": "28kmG0zsccVgkCiTagVdZhVePWV",
"index": "a0",
"type": "TextBlock",
"text": [
{
"type": "heading",
"level": 1,
"content": [
{
"type": "text",
"text": "Color"
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "A color field is used to input a hex color code."
}
]
},
{
"id": "28kmYRWE9qZv5cEHmEmFtHaIzlu",
"type": "CodeBlock",
"code": "color('My color field', {\n initialValue: '#4a63e7'\n})",
"compact": false
}
]
},
{
"id": "28kmocDvCZt990Xf4Ws6ZqBwiGL",
"index": "a1",
"type": "TypesBlock",
"types": "ColorOptions"
}
],
"parents": [
"docs",
"259Ey7LXgqL7ZDGJ8XK1vBJmZrh",
"24yE2mu6Xq959jrP135Sdb4e3lG"
]
}
11 changes: 2 additions & 9 deletions apps/web/content/data/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"root": "data",
"index": "Zy",
"title": "Home",
"parents": [],
"headline": "Structure\ncontent fast",
"byline": "Structure, edit and query content with any web framework.\nFully typed and organized in your repository.",
"action": [
Expand All @@ -15,13 +16,5 @@
"label": "Getting started"
}
],
"links": [
{
"id": "263wlTQJl4GPVikrzGKCxRKuJTg",
"index": "a0",
"type": "entry",
"entry": "20580nQzbOBR3Lt4kIdxyRGglc6",
"title": "Docs"
}
]
"links": []
}
3 changes: 2 additions & 1 deletion apps/web/src/view/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {fromModule, HStack, px, Typo, VStack} from '@alinea/ui'

import {IcRoundOpenInNew} from '@alinea/ui/icons/IcRoundOpenInNew'
import css from './HomePage.module.scss'
import {HomePageSchema} from './HomePage.schema'
import {Hero} from './layout/Hero'

const styles = fromModule(css)

const exampleCode = `schema('Blog', {
const exampleCode = `schema('Blog', {0
BlogEntry: type('Blog entry', {
title: text('Title'),
author: link('Author', {type: 'entry'}),
Expand Down
38 changes: 38 additions & 0 deletions packages/input/color/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@alinea/input.color",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/alineacms/alinea.git"
},
"type": "module",
"exports": {
".": {
"browser": "./dist/view.js",
"default": "./dist/index.js"
},
"./*": "./dist/*.js",
"./index.css": "./dist/index.css"
},
"files": [
"dist"
],
"sideEffects": false,
"typesVersions": {
"*": {
"*": [
"./dist/*"
]
}
},
"dependencies": {
"@alinea/core": "0.0.0",
"@alinea/editor": "0.0.0",
"@alinea/ui": "0.0.0",
"react-colorful": "^5.5.1"
},
"peerDependencies": {
"react": "*"
}
}
35 changes: 35 additions & 0 deletions packages/input/color/src/ColorField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {Field, Label, Shape} from '@alinea/core'

/** Optional settings to configure a color field */
export type ColorOptions = {
/** Width of the field in the dashboard UI (0-1) */
width?: number
/** Add instructional text to a field */
help?: Label
/** Field is optional */
optional?: boolean
/** Display a minimal version */
inline?: boolean
/** A default value */
initialValue?: string
/** List of allowed hex codes */
allowedColors?: Array<string>
}

/** Internal representation of a text field */
export interface ColorField extends Field.Scalar<string> {
label: Label
options: ColorOptions
}

/** Create a text field configuration */
export function createColor(
label: Label,
options: ColorOptions = {}
): ColorField {
return {
shape: Shape.Scalar(label),
label,
options
}
}
82 changes: 82 additions & 0 deletions packages/input/color/src/ColorInput.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.root {
&-button {
display: flex;
align-items: center;
justify-content: center;
height: 25px;
width: 25px;
border-radius: 50%;
border: none;
cursor: pointer;

&-check {
height: 18px;
width: 18px;
}
}

&-popover {
position: relative;

&-button {
border: none;
background-color: var(--alinea-background);
}

&-clear {
margin-left: 8px;
}

&-panel {
position: absolute;
left: 38px;
}

&-color {
width: 38px;
height: 38px;
border-radius: 8px 0 0 8px;

&.is-empty {
box-shadow: var(--alinea-fields-shadow);
background: linear-gradient(
to top left,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0) calc(50% - 0.8px),
var(--alinea-fields-foreground) 50%,
rgba(0, 0, 0, 0) calc(50% + 0.8px),
rgba(0, 0, 0, 0) 100%
),
linear-gradient(
to top right,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0) calc(50% - 0.8px),
var(--alinea-fields-foreground) 50%,
rgba(0, 0, 0, 0) calc(50% + 0.8px),
rgba(0, 0, 0, 0) 100%
);
}
}

&-input {
display: block;
background: transparent;
border: none;
color: inherit;
font: inherit;
width: 100%;
resize: none;
padding: 9px 16px;
border-radius: 0 8px 8px 0;
line-height: 1.5;
background: var(--alinea-fields);
box-shadow: var(--alinea-fields-shadow);

&.is-open,
&:focus {
background: var(--alinea-fields-selected);
outline: 2px solid var(--alinea-fields-focus);
}
}
}
}
121 changes: 121 additions & 0 deletions packages/input/color/src/ColorInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {InputLabel, InputState, useInput} from '@alinea/editor'
import {fromModule, HStack, IconButton} from '@alinea/ui'
import {useContrastColor} from '@alinea/ui/hook/UseContrastColor'
import {IcRoundCheck} from '@alinea/ui/icons/IcRoundCheck'
import {IcRoundClear} from '@alinea/ui/icons/IcRoundClear'
import {IcRoundColorLens} from '@alinea/ui/icons/IcRoundColorLens'
import {Popover} from '@headlessui/react'
import {HexColorInput, HexColorPicker} from 'react-colorful'
import {ColorField} from './ColorField'
import css from './ColorInput.module.scss'

const styles = fromModule(css)

type AllowedColorPickerProps = {
selectedColor: string | undefined
colors: Array<string>
onClick: (color: string) => void
}

function AllowedColorPicker({
selectedColor,
colors,
onClick
}: AllowedColorPickerProps) {
const contrastColor = useContrastColor(selectedColor)
return (
<>
<HStack center gap={8}>
{colors.map(color => (
<button
key={color}
className={styles.root.button()}
style={{backgroundColor: color}}
onClick={() => onClick(color)}
>
{color === selectedColor && (
<IcRoundCheck
className={styles.root.button.check()}
style={{color: contrastColor}}
/>
)}
</button>
))}
{selectedColor && (
<IconButton icon={IcRoundClear} onClick={() => onClick('')} />
)}
</HStack>
</>
)
}

type AllColorPickerProps = {
color: string | undefined
onChange: (color: string) => void
}

function AllColorPicker({color, onChange}: AllColorPickerProps) {
return (
<Popover className={styles.root.popover()}>
{({open}) => (
<>
<Popover.Button className={styles.root.popover.button()}>
<HStack center>
<div
className={styles.root.popover.color({empty: !color})}
style={{backgroundColor: color}}
/>
<HexColorInput
color={color}
onChange={onChange}
className={styles.root.popover.input({open: open})}
/>
{color && (
<IconButton
className={styles.root.popover.clear()}
icon={IcRoundClear}
onClick={() => onChange('')}
/>
)}
</HStack>
</Popover.Button>
<Popover.Panel className={styles.root.popover.panel()}>
<HexColorPicker color={color} onChange={onChange} />
</Popover.Panel>
</>
)}
</Popover>
)
}

export type ColorInputProps = {
state: InputState<InputState.Scalar<string>>
field: ColorField
}

export function ColorInput({state, field}: ColorInputProps) {
const {width, inline, optional, help, initialValue, allowedColors} =
field.options
const [value = initialValue, setValue] = useInput(state)
return (
<InputLabel
asLabel
label={field.label}
optional={optional}
inline={inline}
width={width}
icon={IcRoundColorLens}
help={help}
>
{allowedColors ? (
<AllowedColorPicker
selectedColor={value}
colors={allowedColors}
onClick={setValue}
/>
) : (
<AllColorPicker color={value} onChange={setValue} />
)}
</InputLabel>
)
}
4 changes: 4 additions & 0 deletions packages/input/color/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {createColor} from './ColorField'
export * from './ColorField'
/** Create a check field configuration */
export const color = createColor
6 changes: 6 additions & 0 deletions packages/input/color/src/view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {Field} from '@alinea/core'
import {createColor} from './ColorField'
import {ColorInput} from './ColorInput'
export * from './ColorField'
export * from './ColorInput'
export const color = Field.withView(createColor, ColorInput)
12 changes: 12 additions & 0 deletions packages/ui/src/icons/IcRoundClear.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React, {SVGProps} from 'react'

export function IcRoundClear(props: SVGProps<SVGSVGElement>) {
return (
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M18.3 5.71a.996.996 0 0 0-1.41 0L12 10.59L7.11 5.7A.996.996 0 1 0 5.7 7.11L10.59 12L5.7 16.89a.996.996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996.996 0 1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"
></path>
</svg>
)
}
Loading