Allow custom icons and additional links in left nav
This commit is contained in:
parent
a82f27100e
commit
04098bf96d
@ -62,6 +62,7 @@ collections: # A list of collections the CMS should be able to edit
|
|||||||
|
|
||||||
- name: 'settings'
|
- name: 'settings'
|
||||||
label: 'Settings'
|
label: 'Settings'
|
||||||
|
icon: 'settings'
|
||||||
delete: false # Prevent users from deleting documents in this collection
|
delete: false # Prevent users from deleting documents in this collection
|
||||||
editor:
|
editor:
|
||||||
preview: false
|
preview: false
|
||||||
|
@ -237,6 +237,7 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
CMS.registerAdditionalLink('Example.com', 'https://example.com/', 'new-tab');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import React from 'react';
|
||||||
// Core
|
// Core
|
||||||
import { NetlifyCmsCore as CMS } from 'netlify-cms-core';
|
import { NetlifyCmsCore as CMS } from 'netlify-cms-core';
|
||||||
// Backends
|
// Backends
|
||||||
@ -29,6 +30,7 @@ import NetlifyCmsWidgetColorString from 'netlify-cms-widget-colorstring';
|
|||||||
import image from 'netlify-cms-editor-component-image';
|
import image from 'netlify-cms-editor-component-image';
|
||||||
// Locales
|
// Locales
|
||||||
import * as locales from 'netlify-cms-locales';
|
import * as locales from 'netlify-cms-locales';
|
||||||
|
import { images, Icon } from 'netlify-cms-ui-default';
|
||||||
|
|
||||||
// Register all the things
|
// Register all the things
|
||||||
CMS.registerBackend('git-gateway', GitGatewayBackend);
|
CMS.registerBackend('git-gateway', GitGatewayBackend);
|
||||||
@ -66,3 +68,7 @@ CMS.registerEditorComponent({
|
|||||||
Object.keys(locales).forEach(locale => {
|
Object.keys(locales).forEach(locale => {
|
||||||
CMS.registerLocale(locale, locales[locale]);
|
CMS.registerLocale(locale, locales[locale]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.keys(images).forEach(iconName => {
|
||||||
|
CMS.registerIcon(iconName, <Icon type={iconName} />);
|
||||||
|
});
|
||||||
|
7
packages/netlify-cms-core/index.d.ts
vendored
7
packages/netlify-cms-core/index.d.ts
vendored
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
declare module 'netlify-cms-core' {
|
declare module 'netlify-cms-core' {
|
||||||
import type { ComponentType } from 'react';
|
import type { ComponentType, ReactNode } from 'react';
|
||||||
import type { List, Map } from 'immutable';
|
import type { List, Map } from 'immutable';
|
||||||
import type { Pluggable } from 'unified';
|
import type { Pluggable } from 'unified';
|
||||||
|
|
||||||
@ -578,7 +578,10 @@ declare module 'netlify-cms-core' {
|
|||||||
widgetName: string,
|
widgetName: string,
|
||||||
serializer: CmsWidgetValueSerializer,
|
serializer: CmsWidgetValueSerializer,
|
||||||
) => void;
|
) => void;
|
||||||
resolveWidget: (name: string) => CmsWidget | undefined;
|
registerIcon: (iconName: string, icon: ReactNode) => void;
|
||||||
|
getIcon: (iconName: string) => ReactNode;
|
||||||
|
registerAdditionalLink: (title: string, link: string, iconName?: string) => void;
|
||||||
|
getAdditionalLinks: () => { title: string, link: string, iconName?: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NetlifyCmsCore: CMS;
|
export const NetlifyCmsCore: CMS;
|
||||||
|
@ -10,6 +10,7 @@ import { Icon, components, colors } from 'netlify-cms-ui-default';
|
|||||||
import { searchCollections } from '../../actions/collections';
|
import { searchCollections } from '../../actions/collections';
|
||||||
import CollectionSearch from './CollectionSearch';
|
import CollectionSearch from './CollectionSearch';
|
||||||
import NestedCollection from './NestedCollection';
|
import NestedCollection from './NestedCollection';
|
||||||
|
import { getAdditionalLinks, getIcon } from '../../lib/registry';
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
sidebarNavLinkActive: css`
|
sidebarNavLinkActive: css`
|
||||||
@ -67,6 +68,26 @@ const SidebarNavLink = styled(NavLink)`
|
|||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const AdditionalLink = styled.a`
|
||||||
|
display: flex;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-left: 2px solid #fff;
|
||||||
|
z-index: -1;
|
||||||
|
|
||||||
|
${Icon} {
|
||||||
|
margin-right: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
${styles.sidebarNavLinkActive};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export class Sidebar extends React.Component {
|
export class Sidebar extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
collections: ImmutablePropTypes.map.isRequired,
|
collections: ImmutablePropTypes.map.isRequired,
|
||||||
@ -79,6 +100,14 @@ export class Sidebar extends React.Component {
|
|||||||
|
|
||||||
renderLink = (collection, filterTerm) => {
|
renderLink = (collection, filterTerm) => {
|
||||||
const collectionName = collection.get('name');
|
const collectionName = collection.get('name');
|
||||||
|
const iconName = collection.get('icon');
|
||||||
|
let icon = <Icon type="write" />;
|
||||||
|
if (iconName) {
|
||||||
|
const storedIcon = getIcon(iconName);
|
||||||
|
if (storedIcon) {
|
||||||
|
icon = storedIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (collection.has('nested')) {
|
if (collection.has('nested')) {
|
||||||
return (
|
return (
|
||||||
<li key={collectionName}>
|
<li key={collectionName}>
|
||||||
@ -97,7 +126,60 @@ export class Sidebar extends React.Component {
|
|||||||
activeClassName="sidebar-active"
|
activeClassName="sidebar-active"
|
||||||
data-testid={collectionName}
|
data-testid={collectionName}
|
||||||
>
|
>
|
||||||
<Icon type="write" />
|
{icon}
|
||||||
|
{collection.get('label')}
|
||||||
|
</SidebarNavLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderAdditionalLink = ({ title, url, iconName }) => {
|
||||||
|
let icon = <Icon type="write" />;
|
||||||
|
if (iconName) {
|
||||||
|
const storedIcon = getIcon(iconName);
|
||||||
|
if (storedIcon) {
|
||||||
|
icon = storedIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li key={title}>
|
||||||
|
<AdditionalLink href={url} target="_blank" rel="noopener">
|
||||||
|
{icon}
|
||||||
|
{title}
|
||||||
|
</AdditionalLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderLink = (collection, filterTerm) => {
|
||||||
|
const collectionName = collection.get('name');
|
||||||
|
const iconName = collection.get('icon');
|
||||||
|
let icon = <Icon type="write" />;
|
||||||
|
if (iconName) {
|
||||||
|
const storedIcon = getIcon(iconName);
|
||||||
|
if (storedIcon) {
|
||||||
|
icon = storedIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (collection.has('nested')) {
|
||||||
|
return (
|
||||||
|
<li key={collectionName}>
|
||||||
|
<NestedCollection
|
||||||
|
collection={collection}
|
||||||
|
filterTerm={filterTerm}
|
||||||
|
data-testid={collectionName}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li key={collectionName}>
|
||||||
|
<SidebarNavLink
|
||||||
|
to={`/collections/${collectionName}`}
|
||||||
|
activeClassName="sidebar-active"
|
||||||
|
data-testid={collectionName}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
{collection.get('label')}
|
{collection.get('label')}
|
||||||
</SidebarNavLink>
|
</SidebarNavLink>
|
||||||
</li>
|
</li>
|
||||||
@ -106,6 +188,7 @@ export class Sidebar extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { collections, collection, isSearchEnabled, searchTerm, t, filterTerm } = this.props;
|
const { collections, collection, isSearchEnabled, searchTerm, t, filterTerm } = this.props;
|
||||||
|
const additionalLinks = getAdditionalLinks();
|
||||||
return (
|
return (
|
||||||
<SidebarContainer>
|
<SidebarContainer>
|
||||||
<SidebarHeading>{t('collection.sidebar.collections')}</SidebarHeading>
|
<SidebarHeading>{t('collection.sidebar.collections')}</SidebarHeading>
|
||||||
@ -122,6 +205,7 @@ export class Sidebar extends React.Component {
|
|||||||
.toList()
|
.toList()
|
||||||
.filter(collection => collection.get('hide') !== true)
|
.filter(collection => collection.get('hide') !== true)
|
||||||
.map(collection => this.renderLink(collection, filterTerm))}
|
.map(collection => this.renderLink(collection, filterTerm))}
|
||||||
|
{additionalLinks.map(this.renderAdditionalLink)}
|
||||||
</SidebarNavList>
|
</SidebarNavList>
|
||||||
</SidebarContainer>
|
</SidebarContainer>
|
||||||
);
|
);
|
||||||
|
@ -25,6 +25,8 @@ const registry = {
|
|||||||
templates: {},
|
templates: {},
|
||||||
previewStyles: [],
|
previewStyles: [],
|
||||||
widgets: {},
|
widgets: {},
|
||||||
|
icons: {},
|
||||||
|
additionalLinks: [],
|
||||||
editorComponents: Map(),
|
editorComponents: Map(),
|
||||||
remarkPlugins: [],
|
remarkPlugins: [],
|
||||||
widgetValueSerializers: {},
|
widgetValueSerializers: {},
|
||||||
@ -58,6 +60,10 @@ export default {
|
|||||||
removeEventListener,
|
removeEventListener,
|
||||||
getEventListeners,
|
getEventListeners,
|
||||||
invokeEvent,
|
invokeEvent,
|
||||||
|
registerIcon,
|
||||||
|
getIcon,
|
||||||
|
registerAdditionalLink,
|
||||||
|
getAdditionalLinks
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,3 +290,23 @@ export function registerLocale(locale, phrases) {
|
|||||||
export function getLocale(locale) {
|
export function getLocale(locale) {
|
||||||
return registry.locales[locale];
|
return registry.locales[locale];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icons
|
||||||
|
*/
|
||||||
|
export function registerIcon(name, icon) {
|
||||||
|
registry.icons[name] = icon;
|
||||||
|
}
|
||||||
|
export function getIcon(name) {
|
||||||
|
return registry.icons[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icons
|
||||||
|
*/
|
||||||
|
export function registerAdditionalLink(title, url, iconName) {
|
||||||
|
registry.additionalLinks.push({ title, url, iconName });
|
||||||
|
}
|
||||||
|
export function getAdditionalLinks() {
|
||||||
|
return registry.additionalLinks;
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ import AuthenticationPage from './AuthenticationPage';
|
|||||||
import WidgetPreviewContainer from './WidgetPreviewContainer';
|
import WidgetPreviewContainer from './WidgetPreviewContainer';
|
||||||
import ObjectWidgetTopBar from './ObjectWidgetTopBar';
|
import ObjectWidgetTopBar from './ObjectWidgetTopBar';
|
||||||
import GoBackButton from './GoBackButton';
|
import GoBackButton from './GoBackButton';
|
||||||
|
import images from './Icon/images/_index';
|
||||||
import {
|
import {
|
||||||
fonts,
|
fonts,
|
||||||
colorsRaw,
|
colorsRaw,
|
||||||
@ -63,6 +64,7 @@ export const NetlifyCmsUiDefault = {
|
|||||||
zIndex,
|
zIndex,
|
||||||
reactSelectStyles,
|
reactSelectStyles,
|
||||||
GlobalStyles,
|
GlobalStyles,
|
||||||
|
images,
|
||||||
};
|
};
|
||||||
export {
|
export {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
@ -97,4 +99,5 @@ export {
|
|||||||
reactSelectStyles,
|
reactSelectStyles,
|
||||||
GlobalStyles,
|
GlobalStyles,
|
||||||
GoBackButton,
|
GoBackButton,
|
||||||
|
images,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user