Skip to content

Commit

Permalink
feat(widget-color): add color widget (decaporg#4437)
Browse files Browse the repository at this point in the history
  • Loading branch information
felixboet authored and vladdu committed Jan 26, 2021
1 parent 92c1727 commit a35fbe6
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 1 deletion.
8 changes: 8 additions & 0 deletions dev-test/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'] }
Expand Down
1 change: 1 addition & 0 deletions packages/netlify-cms-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions packages/netlify-cms-app/src/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -55,6 +56,7 @@ CMS.registerWidget([
NetlifyCmsWidgetDate.Widget(),
NetlifyCmsWidgetDatetime.Widget(),
NetlifyCmsWidgetCode.Widget(),
NetlifyCmsWidgetColor.Widget(),
]);
CMS.registerEditorComponent(image);
CMS.registerEditorComponent({
Expand Down
4 changes: 4 additions & 0 deletions packages/netlify-cms-widget-color/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions packages/netlify-cms-widget-color/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions packages/netlify-cms-widget-color/package.json
Original file line number Diff line number Diff line change
@@ -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 changes: 176 additions & 0 deletions packages/netlify-cms-widget-color/src/ColorControl.js
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions packages/netlify-cms-widget-color/src/ColorPreview.js
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions packages/netlify-cms-widget-color/src/index.js
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions packages/netlify-cms-widget-color/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { getConfig } = require('../../scripts/webpack.js');

module.exports = getConfig();
22 changes: 22 additions & 0 deletions website/content/docs/widgets/color.md
Original file line number Diff line number Diff line change
@@ -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 }
```
Loading

0 comments on commit a35fbe6

Please sign in to comment.