feat(widget-color): add color widget (#4437)
This commit is contained in:
parent
f3a8eac816
commit
fed0e82dd3
@ -119,6 +119,14 @@ collections: # A list of collections the CMS should be able to edit
|
||||
- { label: 'Markdown', name: 'markdown', widget: 'markdown' }
|
||||
- { label: 'Datetime', name: 'datetime', widget: 'datetime' }
|
||||
- { label: 'Date', name: 'date', widget: 'date' }
|
||||
- { label: 'Color', name: 'color', widget: 'color' }
|
||||
- {
|
||||
label: 'Color string editable and alpha enabled',
|
||||
name: 'colorEditable',
|
||||
widget: 'color',
|
||||
enableAlpha: true,
|
||||
allowInput: true,
|
||||
}
|
||||
- { label: 'Image', name: 'image', widget: 'image' }
|
||||
- { label: 'File', name: 'file', widget: 'file' }
|
||||
- { label: 'Select', name: 'select', widget: 'select', options: ['a', 'b', 'c'] }
|
||||
|
@ -45,6 +45,7 @@
|
||||
"netlify-cms-ui-default": "^2.11.6",
|
||||
"netlify-cms-widget-boolean": "^2.3.4",
|
||||
"netlify-cms-widget-code": "^1.2.4",
|
||||
"netlify-cms-widget-color": "^1.0.0",
|
||||
"netlify-cms-widget-date": "^2.5.5",
|
||||
"netlify-cms-widget-datetime": "^2.6.5",
|
||||
"netlify-cms-widget-file": "^2.7.4",
|
||||
|
@ -25,6 +25,7 @@ import NetlifyCmsWidgetMap from 'netlify-cms-widget-map';
|
||||
import NetlifyCmsWidgetDate from 'netlify-cms-widget-date';
|
||||
import NetlifyCmsWidgetDatetime from 'netlify-cms-widget-datetime';
|
||||
import NetlifyCmsWidgetCode from 'netlify-cms-widget-code';
|
||||
import NetlifyCmsWidgetColor from 'netlify-cms-widget-color';
|
||||
|
||||
// Editor Components
|
||||
import image from 'netlify-cms-editor-component-image';
|
||||
@ -55,6 +56,7 @@ CMS.registerWidget([
|
||||
NetlifyCmsWidgetDate.Widget(),
|
||||
NetlifyCmsWidgetDatetime.Widget(),
|
||||
NetlifyCmsWidgetCode.Widget(),
|
||||
NetlifyCmsWidgetColor.Widget(),
|
||||
]);
|
||||
CMS.registerEditorComponent(image);
|
||||
CMS.registerEditorComponent({
|
||||
|
4
packages/netlify-cms-widget-color/CHANGELOG.md
Normal file
4
packages/netlify-cms-widget-color/CHANGELOG.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
11
packages/netlify-cms-widget-color/README.md
Normal file
11
packages/netlify-cms-widget-color/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Docs coming soon!
|
||||
|
||||
Netlify CMS was recently converted from a single npm package to a "monorepo" of over 20 packages.
|
||||
That's over 20 Readme's! We haven't created one for this package yet, but we will soon.
|
||||
|
||||
In the meantime, you can:
|
||||
|
||||
1. Check out the [main readme](https://github.com/netlify/netlify-cms/#readme) or the [documentation
|
||||
site](https://www.netlifycms.org) for more info.
|
||||
2. Reach out to the [community chat](https://netlifycms.org/chat/) if you need help.
|
||||
3. Help out and [write the readme yourself](https://github.com/netlify/netlify-cms/edit/master/packages/netlify-cms-widget-string/README.md)!
|
34
packages/netlify-cms-widget-color/package.json
Normal file
34
packages/netlify-cms-widget-color/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "netlify-cms-widget-color",
|
||||
"description": "Widget for editing color strings in Netlify CMS.",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://www.netlifycms.org/docs/widgets/#color",
|
||||
"repository": "https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-widget-color",
|
||||
"bugs": "https://github.com/netlify/netlify-cms/issues",
|
||||
"module": "dist/esm/index.js",
|
||||
"main": "dist/netlify-cms-widget-color.js",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"netlify",
|
||||
"netlify-cms",
|
||||
"widget",
|
||||
"color"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"develop": "yarn build:esm --watch",
|
||||
"build": "cross-env NODE_ENV=production webpack",
|
||||
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-color": "^2.18.1",
|
||||
"validate-color": "^2.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/core": "^10.0.35",
|
||||
"@emotion/styled": "^10.0.27",
|
||||
"netlify-cms-ui-default": "^2.6.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.4"
|
||||
}
|
||||
}
|
176
packages/netlify-cms-widget-color/src/ColorControl.js
Normal file
176
packages/netlify-cms-widget-color/src/ColorControl.js
Normal file
@ -0,0 +1,176 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from '@emotion/styled';
|
||||
import ChromePicker from 'react-color';
|
||||
import validateColor from 'validate-color';
|
||||
import { zIndex } from 'netlify-cms-ui-default';
|
||||
|
||||
const ClearIcon = () => (
|
||||
<svg height="20" width="20" viewBox="0 0 20 20" aria-hidden="true" focusable="false">
|
||||
<path
|
||||
fill="rgb(122, 130, 145)"
|
||||
d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ClearButton = styled.div`
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
z-index: ${zIndex.zIndex1000};
|
||||
padding: 8px;
|
||||
margin-top: 11px;
|
||||
`;
|
||||
|
||||
const ClearButtonWrapper = styled.div`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
// color swatch background with checkerboard to display behind transparent colors
|
||||
const ColorSwatchBackground = styled.div`
|
||||
position: absolute;
|
||||
z-index: ${zIndex.zIndex1};
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==');
|
||||
height: 38px;
|
||||
width: 48px;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
const ColorSwatch = styled.div`
|
||||
position: absolute;
|
||||
z-index: ${zIndex.zIndex2};
|
||||
background: ${props => props.background};
|
||||
cursor: pointer;
|
||||
height: 38px;
|
||||
width: 48px;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
border-radius: 5px;
|
||||
border: 2px solid rgb(223, 223, 227);
|
||||
text-align: center;
|
||||
font-size: 27px;
|
||||
line-height: 1;
|
||||
padding-top: 4px;
|
||||
user-select: none;
|
||||
color: ${props => props.color};
|
||||
`;
|
||||
|
||||
const ColorPickerContainer = styled.div`
|
||||
position: absolute;
|
||||
z-index: ${zIndex.zIndex1000};
|
||||
margin-top: 48px;
|
||||
margin-left: 12px;
|
||||
`;
|
||||
|
||||
// fullscreen div to close color picker when clicking outside of picker
|
||||
const ClickOutsideDiv = styled.div`
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
`;
|
||||
|
||||
export default class ColorControl extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
forID: PropTypes.string,
|
||||
value: PropTypes.node,
|
||||
classNameWrapper: PropTypes.string.isRequired,
|
||||
setActiveStyle: PropTypes.func.isRequired,
|
||||
setInactiveStyle: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
state = {
|
||||
showColorPicker: false,
|
||||
};
|
||||
// show/hide color picker
|
||||
handleClick = () => {
|
||||
this.setState({ showColorPicker: !this.state.showColorPicker });
|
||||
};
|
||||
handleClear = () => {
|
||||
this.props.onChange('');
|
||||
};
|
||||
handleClose = () => {
|
||||
this.setState({ showColorPicker: false });
|
||||
};
|
||||
handleChange = color => {
|
||||
const formattedColor =
|
||||
color.rgb.a < 1
|
||||
? `rgba(${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b}, ${color.rgb.a})`
|
||||
: color.hex;
|
||||
this.props.onChange(formattedColor);
|
||||
};
|
||||
render() {
|
||||
const {
|
||||
forID,
|
||||
value,
|
||||
field,
|
||||
onChange,
|
||||
classNameWrapper,
|
||||
setActiveStyle,
|
||||
setInactiveStyle,
|
||||
} = this.props;
|
||||
|
||||
const allowInput = field.get('allowInput', false);
|
||||
|
||||
// clear button is not displayed if allowInput: true
|
||||
const showClearButton = !allowInput && value;
|
||||
|
||||
return (
|
||||
<>
|
||||
{' '}
|
||||
{showClearButton && (
|
||||
<ClearButtonWrapper>
|
||||
<ClearButton onClick={this.handleClear}>
|
||||
<ClearIcon />
|
||||
</ClearButton>
|
||||
</ClearButtonWrapper>
|
||||
)}
|
||||
<ColorSwatchBackground />
|
||||
<ColorSwatch
|
||||
background={validateColor(this.props.value) ? this.props.value : '#fff'}
|
||||
color={validateColor(this.props.value) ? 'rgba(255, 255, 255, 0)' : 'rgb(223, 223, 227)'}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
?
|
||||
</ColorSwatch>
|
||||
{this.state.showColorPicker && (
|
||||
<ColorPickerContainer>
|
||||
<ClickOutsideDiv onClick={this.handleClose} />
|
||||
<ChromePicker
|
||||
color={value || ''}
|
||||
onChange={this.handleChange}
|
||||
disableAlpha={!field.get('enableAlpha', false)}
|
||||
/>
|
||||
</ColorPickerContainer>
|
||||
)}
|
||||
<input
|
||||
// text input with padding left for the color swatch
|
||||
type="text"
|
||||
id={forID}
|
||||
className={classNameWrapper}
|
||||
value={value || ''}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
onFocus={setActiveStyle}
|
||||
onBlur={setInactiveStyle}
|
||||
style={{
|
||||
paddingLeft: '75px',
|
||||
paddingRight: '70px',
|
||||
color: !allowInput && '#bbb',
|
||||
}}
|
||||
// make readonly and open color picker on click if set to allowInput: false
|
||||
onClick={!allowInput ? this.handleClick : undefined}
|
||||
readOnly={!allowInput}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
11
packages/netlify-cms-widget-color/src/ColorPreview.js
Normal file
11
packages/netlify-cms-widget-color/src/ColorPreview.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
|
||||
|
||||
const ColorPreview = ({ value }) => <WidgetPreviewContainer>{value}</WidgetPreviewContainer>;
|
||||
|
||||
ColorPreview.propTypes = {
|
||||
value: PropTypes.node,
|
||||
};
|
||||
|
||||
export default ColorPreview;
|
12
packages/netlify-cms-widget-color/src/index.js
Normal file
12
packages/netlify-cms-widget-color/src/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
import controlComponent from './ColorControl';
|
||||
import previewComponent from './ColorPreview';
|
||||
|
||||
const Widget = (opts = {}) => ({
|
||||
name: 'color',
|
||||
controlComponent,
|
||||
previewComponent,
|
||||
...opts,
|
||||
});
|
||||
|
||||
export const NetlifyCmsWidgetColor = { Widget, controlComponent, previewComponent };
|
||||
export default NetlifyCmsWidgetColor;
|
3
packages/netlify-cms-widget-color/webpack.config.js
Normal file
3
packages/netlify-cms-widget-color/webpack.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
const { getConfig } = require('../../scripts/webpack.js');
|
||||
|
||||
module.exports = getConfig();
|
22
website/content/docs/widgets/color.md
Normal file
22
website/content/docs/widgets/color.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
label: 'Color'
|
||||
title: color
|
||||
---
|
||||
|
||||
The color widget translates a color picker to a color string.
|
||||
|
||||
- **Name:** `color`
|
||||
- **UI:** color picker
|
||||
- **Data type:** string
|
||||
- **Options:**
|
||||
- `default`: accepts a string; defaults to an empty string. Sets the default value
|
||||
- `allowInput`: accepts a boolean, defaults to `false`. Allows manual editing of the color input value
|
||||
- `enableAlpha`: accepts a boolean, defaults to `false`. Enables Alpha editing
|
||||
- **Example:**
|
||||
```yaml
|
||||
- { label: 'Color', name: 'color', widget: 'color' }
|
||||
```
|
||||
- **Example:**
|
||||
```yaml
|
||||
- { label: 'Color', name: 'color', widget: 'color', enableAlpha: true, allowInput: true }
|
||||
```
|
41
yarn.lock
41
yarn.lock
@ -1527,6 +1527,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c"
|
||||
integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==
|
||||
|
||||
"@icons/material@^0.2.4":
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"
|
||||
integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
@ -11706,7 +11711,7 @@ lodash.uniq@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
lodash@^4.1.1, lodash@^4.11.2, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1, lodash@~4.17.10:
|
||||
lodash@^4.0.1, lodash@^4.1.1, lodash@^4.11.2, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1, lodash@~4.17.10:
|
||||
version "4.17.20"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
||||
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
||||
@ -11939,6 +11944,11 @@ markdown-to-jsx@^6.11.4:
|
||||
prop-types "^15.6.2"
|
||||
unquote "^1.1.0"
|
||||
|
||||
material-colors@^1.2.1:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
|
||||
integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==
|
||||
|
||||
mathml-tag-names@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
|
||||
@ -14335,6 +14345,18 @@ react-codemirror2@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-7.2.1.tgz#38dab492fcbe5fb8ebf5630e5bb7922db8d3a10c"
|
||||
integrity sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==
|
||||
|
||||
react-color@^2.18.1:
|
||||
version "2.18.1"
|
||||
resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.18.1.tgz#2cda8cc8e06a9e2c52ad391a30ddad31972472f4"
|
||||
integrity sha512-X5XpyJS6ncplZs74ak0JJoqPi+33Nzpv5RYWWxn17bslih+X7OlgmfpmGC1fNvdkK7/SGWYf1JJdn7D2n5gSuQ==
|
||||
dependencies:
|
||||
"@icons/material" "^0.2.4"
|
||||
lodash "^4.17.11"
|
||||
material-colors "^1.2.1"
|
||||
prop-types "^15.5.10"
|
||||
reactcss "^1.2.0"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
react-datetime@^2.16.3:
|
||||
version "2.16.3"
|
||||
resolved "https://registry.yarnpkg.com/react-datetime/-/react-datetime-2.16.3.tgz#7f9ac7d4014a939c11c761d0c22d1fb506cb505e"
|
||||
@ -14762,6 +14784,13 @@ react@^16.12.0, react@^16.8.3, react@^16.8.4:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
reactcss@^1.2.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
|
||||
integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==
|
||||
dependencies:
|
||||
lodash "^4.0.1"
|
||||
|
||||
read-cmd-shim@^1.0.1:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16"
|
||||
@ -17156,6 +17185,11 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
tinycolor2@^1.4.1:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
||||
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
@ -18052,6 +18086,11 @@ v8-to-istanbul@^6.0.1:
|
||||
convert-source-map "^1.6.0"
|
||||
source-map "^0.7.3"
|
||||
|
||||
validate-color@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/validate-color/-/validate-color-2.1.0.tgz#ba32664db859a197c19520b50316b8b8d5ffa0bc"
|
||||
integrity sha512-/78x9MDsanOisNfa4UxombQkPCE2A/hmFUtgunwGutpAgTRnhKvOhzJ4KeSDunbKEla6T+u0OlHd1hWCLVhSdA==
|
||||
|
||||
validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||
|
Loading…
x
Reference in New Issue
Block a user