Skip to content

Commit

Permalink
Merge pull request #199 from performant-software/feature/cdc1_projects
Browse files Browse the repository at this point in the history
CDC #2 - Projects
  • Loading branch information
dleadbetter authored Aug 31, 2023
2 parents dee4750 + 5c2c05c commit 50fbe74
Show file tree
Hide file tree
Showing 19 changed files with 633 additions and 29 deletions.
6 changes: 3 additions & 3 deletions packages/controlled-vocabulary/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/controlled-vocabulary",
"version": "1.0.18",
"version": "1.0.19",
"description": "A package of components to allow user to configure dropdown elements. Use with the \"controlled_vocabulary\" gem.",
"license": "MIT",
"main": "./build/index.js",
Expand All @@ -12,8 +12,8 @@
"build": "webpack --mode production && flow-copy-source -v src types"
},
"dependencies": {
"@performant-software/semantic-components": "^1.0.18",
"@performant-software/shared-components": "^1.0.18",
"@performant-software/semantic-components": "^1.0.19",
"@performant-software/shared-components": "^1.0.19",
"i18next": "^21.9.2",
"semantic-ui-react": "^2.1.2",
"underscore": "^1.13.2"
Expand Down
4 changes: 2 additions & 2 deletions packages/semantic-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/semantic-components",
"version": "1.0.18",
"version": "1.0.19",
"description": "A package of shared components based on the Semantic UI Framework.",
"license": "MIT",
"main": "./build/index.js",
Expand All @@ -12,7 +12,7 @@
"build": "webpack --mode production && flow-copy-source -v src types"
},
"dependencies": {
"@performant-software/shared-components": "^1.0.18",
"@performant-software/shared-components": "^1.0.19",
"@react-google-maps/api": "^2.8.1",
"axios": "^0.26.1",
"i18next": "^19.4.4",
Expand Down
Empty file.
69 changes: 69 additions & 0 deletions packages/semantic-ui/src/components/BreadcrumbItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// @flow

import React, { useEffect, useState, type ComponentType } from 'react';
import { Breadcrumb, Loader } from 'semantic-ui-react';

type Props = {
active: boolean,
as?: ComponentType<any>,
id?: number,
label?: string,
name: string,
onLoad: (id: number, name: string) => Promise<any>,
url: string
};

const BreadcrumbItem: ComponentType<any> = (props: Props) => {
const [loading, setLoading] = useState(false);
const [name, setName] = useState(null);

/**
* Sets or clears the name attribute on the state.
*/
useEffect(() => {
if (props.id) {
props
.onLoad(props.id, props.name)
.then((n) => setName(n))
.finally(() => setLoading(false));

setLoading(true);
} else {
setName(null);
}
}, [props.id, props.name]);

return (
<>
<Breadcrumb.Section
active={props.active && !props.id}
as={props.as}
to={`/${props.url}`}
>
{ props.label }
</Breadcrumb.Section>
{ props.id && (
<Breadcrumb.Divider
icon='right chevron'
/>
)}
{ loading && (
<Loader
active
inline
/>
)}
{ name && props.id && (
<Breadcrumb.Section
active={props.active}
as={props.as}
to={`/${props.url}/${props.id}`}
>
{ name }
</Breadcrumb.Section>
)}
</>
);
};

export default BreadcrumbItem;
4 changes: 4 additions & 0 deletions packages/semantic-ui/src/components/Breadcrumbs.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.ui.breadcrumb:first-child {
margin-bottom: 1em;
margin-top: 0.5em;
}
111 changes: 111 additions & 0 deletions packages/semantic-ui/src/components/Breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// @flow

import React, { useCallback, useMemo, type ComponentType } from 'react';
import { Breadcrumb } from 'semantic-ui-react';
import _ from 'underscore';
import BreadcrumbItem from './BreadcrumbItem';
import './Breadcrumbs.css';

const URL_DELIMITER = '/';

type Props = {
/**
* Alternate component to use to render the breadcrumb.
*/
as?: ComponentType<any>,

/**
* A key-value pair of types to labels to match the `pathname`.
*/
labels: { key: string, value: string },

/**
* Callback fired to lookup the name of the passed breadcrumb item.
*/
onLoad: (id: number, name: string) => Promise<any>,

/**
* The URL for which to generate the breadcrumb.
*/
pathname: string
};

/**
* This component can be used to render a breadcrumb for the passed URL.
*/
const Breadcrumbs: ComponentType<any> = (props: Props) => {
/**
* Returns true if the passed string contains only digits.
*
* @type {function(*): boolean}
*/
const isNumeric = useCallback((str: string) => /^\d+$/.test(str), []);

/**
* Sets the items to display based on the URL path.
*
* @type {[]}
*/
const items = useMemo(() => {
const value = [];

const path = props.pathname.split(URL_DELIMITER).splice(1);

for (let i = 0; i < path.length; i += 1) {
const key = path[i];
const id = path[i + 1];
const url = path.slice(0, i + 1).join(URL_DELIMITER);

/*
* If the item in the path is non-numeric, we'll add it as the item label.
* If the next item is numeric, we'll add it as the ID.
* If the next item is non-numeric, it'll be added as a separate label on the next iteration.
*/
if (!isNumeric(key)) {
const item = { key, url, id: undefined };

if (isNumeric(id)) {
item.id = id;
}

value.push(item);
}
}

return value;
}, [props.pathname]);

/**
* Returns true if there are more items to display.
*
* @type {function(*): boolean}
*/
const hasMore = useCallback((index: number) => index < (items.length - 1), [items]);

return (
<Breadcrumb
size='large'
>
{ _.map(items, (item, index) => (
<>
<BreadcrumbItem
active={!hasMore(index)}
as={props.as}
id={item.id}
label={props.labels[item.key]}
name={item.key}
onLoad={props.onLoad}
url={item.url}
/>
{ hasMore(index) && (
<Breadcrumb.Divider
icon='right chevron'
/>
)}
</>
))}
</Breadcrumb>
);
};

export default Breadcrumbs;
47 changes: 30 additions & 17 deletions packages/semantic-ui/src/components/ItemsToggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ const useItemsToggle = (WrappedComponent: ComponentType<any>) => (
return sort && sort.text;
}

/**
* Returns true if the component should be hidden.
*
* @returns {boolean|*}
*/
isHidden() {
return this.props.hideToggle && _.isEmpty(this.props.sort);
}

/**
* Calls the onSort prop.
*
Expand Down Expand Up @@ -98,7 +107,7 @@ const useItemsToggle = (WrappedComponent: ComponentType<any>) => (
* @returns {*}
*/
render() {
const renderListHeader = this.props.hideToggle
const renderListHeader = this.isHidden()
? undefined
: this.renderHeader.bind(this);

Expand All @@ -121,27 +130,31 @@ const useItemsToggle = (WrappedComponent: ComponentType<any>) => (
* @returns {*}
*/
renderHeader() {
if (this.props.hideToggle) {
if (this.isHidden()) {
return null;
}

return (
<>
<Button
active={this.state.view === Views.list}
aria-label='List View'
basic
icon='list'
onClick={() => this.setState({ view: Views.list })}
/>
<Button
active={this.state.view === Views.grid}
aria-label='Grid View'
basic
icon='grid layout'
onClick={() => this.setState({ view: Views.grid })}
/>
{ this.props.sort && this.props.sort.length > 1 && this.props.onSort && (
{ !this.props.hideToggle && (
<>
<Button
active={this.state.view === Views.list}
aria-label='List View'
basic
icon='list'
onClick={() => this.setState({ view: Views.list })}
/>
<Button
active={this.state.view === Views.grid}
aria-label='Grid View'
basic
icon='grid layout'
onClick={() => this.setState({ view: Views.grid })}
/>
</>
)}
{ !_.isEmpty(this.props.sort) && this.props.onSort && (
<Button.Group
basic
style={{
Expand Down
7 changes: 7 additions & 0 deletions packages/semantic-ui/src/components/SimpleEditPage.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.simple-edit-page.ui.grid {
padding-bottom: 2.5em;
}

.simple-edit-page .menu .item.button-container > .button {
margin-right: 0.25em;
}
Loading

0 comments on commit 50fbe74

Please sign in to comment.