Skip to content

Commit 43fd4e3

Browse files
committed
feat: add uuid widget
Add a uuid widget which supports options: * read_only (bool) * prefix (string) * use_b32_encoding (bool) close: decaporg#1975
1 parent 0c5efd1 commit 43fd4e3

File tree

14 files changed

+184
-2
lines changed

14 files changed

+184
-2
lines changed

dev-test/backends/test/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ collections: # A list of collections the CMS should be able to edit
118118
search_fields: ['title', 'body']
119119
value_field: 'title'
120120
- { label: 'Title', name: 'title', widget: 'string' }
121+
- { label: 'ID', name: 'uuid', widget: 'uuid', read_only: true, prefix: '', use_b32_encode: false }
121122
- { label: 'Boolean', name: 'boolean', widget: 'boolean', default: true }
122123
- { label: 'Map', name: 'map', widget: 'map' }
123124
- { label: 'Text', name: 'text', widget: 'text', hint: 'Plain text, not markdown' }

dev-test/backends/test/index.html

+1-1
Large diffs are not rendered by default.

dev-test/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ collections: # A list of collections the CMS should be able to edit
118118
search_fields: ['title', 'body']
119119
value_field: 'title'
120120
- { label: 'Title', name: 'title', widget: 'string' }
121+
- { label: 'ID', name: 'uuid', widget: 'uuid', read_only: true, prefix: '', use_b32_encode: false }
121122
- { label: 'Boolean', name: 'boolean', widget: 'boolean', default: true }
122123
- { label: 'Map', name: 'map', widget: 'map' }
123124
- { label: 'Text', name: 'text', widget: 'text', hint: 'Plain text, not markdown' }

dev-test/index.html

+1-1
Large diffs are not rendered by default.

packages/netlify-cms-app/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"dependencies": {
3030
"@emotion/core": "^10.0.35",
3131
"@emotion/styled": "^10.0.27",
32+
"base32-encode": "^2.0.0",
3233
"codemirror": "^5.46.0",
34+
"hex-to-array-buffer": "^2.0.0",
3335
"immutable": "^3.7.6",
3436
"lodash": "^4.17.11",
3537
"moment": "^2.24.0",
@@ -63,6 +65,7 @@
6365
"netlify-cms-widget-select": "^2.8.2",
6466
"netlify-cms-widget-string": "^2.3.0",
6567
"netlify-cms-widget-text": "^2.4.1",
68+
"netlify-cms-widget-uuid": "^0.1.0",
6669
"prop-types": "^15.7.2",
6770
"react-immutable-proptypes": "^2.1.0",
6871
"uuid": "^3.3.2"

packages/netlify-cms-app/src/extensions.js

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import NetlifyCmsWidgetDate from 'netlify-cms-widget-date';
2525
import NetlifyCmsWidgetDatetime from 'netlify-cms-widget-datetime';
2626
import NetlifyCmsWidgetCode from 'netlify-cms-widget-code';
2727
import NetlifyCmsWidgetColorString from 'netlify-cms-widget-colorstring';
28+
import NetlifyCmsWidgetUuid from 'netlify-cms-widget-uuid';
2829
// Editor Components
2930
import image from 'netlify-cms-editor-component-image';
3031
// Locales
@@ -55,6 +56,7 @@ CMS.registerWidget([
5556
NetlifyCmsWidgetDatetime.Widget(),
5657
NetlifyCmsWidgetCode.Widget(),
5758
NetlifyCmsWidgetColorString.Widget(),
59+
NetlifyCmsWidgetUuid.Widget(),
5860
]);
5961
CMS.registerEditorComponent(image);
6062
CMS.registerEditorComponent({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5+
6+
7+
**Note:** Version bump only for package netlify-cms-widget-uuid
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Docs coming soon!
2+
3+
Netlify CMS was recently converted from a single npm package to a "monorepo" of over 20 packages.
4+
That's over 20 Readme's! We haven't created one for this package yet, but we will soon.
5+
6+
In the meantime, you can:
7+
8+
1. Check out the [main readme](https://github.com/netlify/netlify-cms/#readme) or the [documentation
9+
site](https://www.netlifycms.org) for more info.
10+
2. Reach out to the [community chat](https://netlifycms.org/chat/) if you need help.
11+
3. Help out and [write the readme yourself](https://github.com/netlify/netlify-cms/edit/master/packages/netlify-cms-widget-string/README.md)!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "netlify-cms-widget-uuid",
3+
"description": "Widget for uuid values in Netlify CMS.",
4+
"version": "0.1.0",
5+
"homepage": "https://www.netlifycms.org/docs/widgets/#uuid",
6+
"repository": "https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-widget-uuid",
7+
"bugs": "https://github.com/netlify/netlify-cms/issues",
8+
"module": "dist/esm/index.js",
9+
"main": "dist/netlify-cms-widget-uuid.js",
10+
"license": "MIT",
11+
"keywords": [
12+
"netlify",
13+
"netlify-cms",
14+
"widget",
15+
"uuid"
16+
],
17+
"sideEffects": false,
18+
"scripts": {
19+
"develop": "yarn build:esm --watch",
20+
"build": "cross-env NODE_ENV=production webpack",
21+
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward"
22+
},
23+
"peerDependencies": {
24+
"base32-encode": "^2.0.0",
25+
"hex-to-array-buffer": "^2.0.0",
26+
"netlify-cms-ui-default": "^2.12.1",
27+
"prop-types": "^15.7.2",
28+
"react": "^16.8.4 || ^17.0.0"
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import base32Encode from 'base32-encode';
4+
import hexToArrayBuffer from 'hex-to-array-buffer';
5+
6+
export default class UuidControl extends React.Component {
7+
static propTypes = {
8+
onChange: PropTypes.func.isRequired,
9+
forID: PropTypes.string,
10+
value: PropTypes.node,
11+
classNameWrapper: PropTypes.string.isRequired,
12+
setActiveStyle: PropTypes.func.isRequired,
13+
setInactiveStyle: PropTypes.func.isRequired,
14+
};
15+
16+
// The selection to maintain for the input element
17+
_sel = 0;
18+
19+
// The input element ref
20+
_el = null;
21+
22+
// NOTE: This prevents the cursor from jumping to the end of the text for
23+
// nested inputs. In other words, this is not an issue on top-level text
24+
// fields such as the `title` of a collection post. However, it becomes an
25+
// issue on fields nested within other components, namely widgets nested
26+
// within a `markdown` widget. For example, the alt text on a block image
27+
// within markdown.
28+
// SEE: https://github.com/netlify/netlify-cms/issues/4539
29+
// SEE: https://github.com/netlify/netlify-cms/issues/3578
30+
componentDidUpdate() {
31+
if (this._el && this._el.selectionStart !== this._sel) {
32+
this._el.setSelectionRange(this._sel, this._sel);
33+
}
34+
}
35+
36+
// componentDidMount is used for generate a UUID when the page loads for the first time
37+
componentDidMount() {
38+
const { value, field, onChange } = this.props;
39+
if (!value) {
40+
const prefix = field.get('prefix', '');
41+
const useB32Encode = field.get('use_b32_encode', false);
42+
const uuid = crypto.randomUUID();
43+
const uuidFormatted = useB32Encode ? this.uuidToB32(uuid) : uuid;
44+
onChange(prefix + uuidFormatted);
45+
}
46+
}
47+
48+
uuidToB32 = uuid => {
49+
const bytes = hexToArrayBuffer(uuid.replace(/-/g, '') || '');
50+
const encodedUUID = base32Encode(bytes, 'RFC4648', { padding: false });
51+
return encodedUUID.toLowerCase();
52+
};
53+
54+
handleChange = e => {
55+
this._sel = e.target.selectionStart;
56+
this.props.onChange(e.target.value);
57+
};
58+
59+
render() {
60+
const { field, forID, value, classNameWrapper, setActiveStyle, setInactiveStyle } = this.props;
61+
const readOnly = field.get('read_only', true);
62+
63+
return (
64+
<input
65+
ref={el => {
66+
this._el = el;
67+
}}
68+
type="text"
69+
id={forID}
70+
readOnly={readOnly}
71+
style={{ fontFamily: 'monospace', opacity: readOnly ? '0.5' : '1.0' }}
72+
className={classNameWrapper}
73+
value={value || ''}
74+
onChange={this.handleChange}
75+
onFocus={setActiveStyle}
76+
onBlur={setInactiveStyle}
77+
/>
78+
);
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
4+
5+
function UuidPreview({ value }) {
6+
return <WidgetPreviewContainer>{value}</WidgetPreviewContainer>;
7+
}
8+
9+
UuidPreview.propTypes = {
10+
value: PropTypes.node,
11+
};
12+
13+
export default UuidPreview;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import controlComponent from './UuidControl';
2+
import previewComponent from './UuidPreview';
3+
4+
function Widget(opts = {}) {
5+
return {
6+
name: 'uuid',
7+
controlComponent,
8+
previewComponent,
9+
...opts,
10+
};
11+
}
12+
13+
export const NetlifyCmsWidgetUuid = { Widget, controlComponent, previewComponent };
14+
export default NetlifyCmsWidgetUuid;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { getConfig } = require('../../scripts/webpack.js');
2+
3+
module.exports = getConfig();

yarn.lock

+17
Original file line numberDiff line numberDiff line change
@@ -5353,6 +5353,13 @@ balanced-match@^2.0.0:
53535353
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
53545354
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
53555355

5356+
base32-encode@^2.0.0:
5357+
version "2.0.0"
5358+
resolved "https://registry.yarnpkg.com/base32-encode/-/base32-encode-2.0.0.tgz#f029bea7d1db2ecc95df5c1d0a6038a39354029a"
5359+
integrity sha512-mlmkfc2WqdDtMl/id4qm3A7RjW6jxcbAoMjdRmsPiwQP0ufD4oXItYMnPgVHe80lnAIy+1xwzhHE1s4FoIceSw==
5360+
dependencies:
5361+
to-data-view "^2.0.0"
5362+
53565363
base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1:
53575364
version "1.5.1"
53585365
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@@ -9790,6 +9797,11 @@ he@^1.2.0:
97909797
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
97919798
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
97929799

9800+
hex-to-array-buffer@^2.0.0:
9801+
version "2.0.0"
9802+
resolved "https://registry.yarnpkg.com/hex-to-array-buffer/-/hex-to-array-buffer-2.0.0.tgz#82c66b3fa8b8f99d74cbae430a5c01dee678117c"
9803+
integrity sha512-svtomp6qK6DL7TgteiPmImS/FQFb5yJSt17zseS8qBcYrJLkieFJKn2a2FBjRG9BYpy9uq2VeEBh6rRc16xh8w==
9804+
97939805
highlight.js@~9.13.0:
97949806
version "9.13.1"
97959807
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
@@ -17763,6 +17775,11 @@ to-arraybuffer@^1.0.0:
1776317775
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
1776417776
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
1776517777

17778+
to-data-view@^2.0.0:
17779+
version "2.0.0"
17780+
resolved "https://registry.yarnpkg.com/to-data-view/-/to-data-view-2.0.0.tgz#4cc3f5c9eb59514a7436fc54c587c3c34c9b1d60"
17781+
integrity sha512-RGEM5KqlPHr+WVTPmGNAXNeFEmsBnlkxXaIfEpUYV0AST2Z5W1EGq9L/MENFrMMmL2WQr1wjkmZy/M92eKhjYA==
17782+
1776617783
to-fast-properties@^1.0.3:
1776717784
version "1.0.3"
1776817785
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"

0 commit comments

Comments
 (0)