ywana-core8 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +1 -0
- package/dist/index.modern.js +2 -0
- package/dist/index.modern.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +27 -0
- package/publish.sh +5 -0
- package/src/css/fonts.css +162 -0
- package/src/css/html.css +36 -0
- package/src/css/theme.css +89 -0
- package/src/css/theme_dark.css +85 -0
- package/src/css/theme_light.css +87 -0
- package/src/domain/CollectionPage.css +34 -0
- package/src/domain/CollectionPage.js +346 -0
- package/src/domain/ContentEditor.css +174 -0
- package/src/domain/ContentEditor.js +425 -0
- package/src/domain/ContentForm.js +74 -0
- package/src/domain/ContentType.js +187 -0
- package/src/domain/CreateContentDialog.js +59 -0
- package/src/domain/EditContentDialog.js +50 -0
- package/src/domain/TablePage.css +29 -0
- package/src/domain/TablePage.js +395 -0
- package/src/domain/index.js +5 -0
- package/src/fonts/Assistant-Bold.ttf +0 -0
- package/src/fonts/Assistant-ExtraBold.ttf +0 -0
- package/src/fonts/Assistant-ExtraLight.ttf +0 -0
- package/src/fonts/Assistant-Light.ttf +0 -0
- package/src/fonts/Assistant-Medium.ttf +0 -0
- package/src/fonts/Assistant-Regular.ttf +0 -0
- package/src/fonts/Assistant-SemiBold.ttf +0 -0
- package/src/fonts/Assistant-VariableFont_wght.ttf +0 -0
- package/src/html/button.css +79 -0
- package/src/html/button.js +26 -0
- package/src/html/checkbox.css +51 -0
- package/src/html/checkbox.js +33 -0
- package/src/html/chip.css +63 -0
- package/src/html/chip.js +39 -0
- package/src/html/form.css +17 -0
- package/src/html/form.js +80 -0
- package/src/html/header.css +64 -0
- package/src/html/header.js +30 -0
- package/src/html/icon.css +53 -0
- package/src/html/icon.js +21 -0
- package/src/html/index.js +18 -0
- package/src/html/list.css +72 -0
- package/src/html/list.js +78 -0
- package/src/html/menu.css +76 -0
- package/src/html/menu.js +80 -0
- package/src/html/progress.css +20 -0
- package/src/html/progress.js +27 -0
- package/src/html/property.css +18 -0
- package/src/html/property.js +16 -0
- package/src/html/radio.css +50 -0
- package/src/html/radio.js +25 -0
- package/src/html/section.css +6 -0
- package/src/html/section.js +31 -0
- package/src/html/tab.css +45 -0
- package/src/html/tab.js +68 -0
- package/src/html/table.css +56 -0
- package/src/html/table.js +186 -0
- package/src/html/text.js +20 -0
- package/src/html/textfield-outlined.css +52 -0
- package/src/html/textfield.css +130 -0
- package/src/html/textfield.js +99 -0
- package/src/html/tokenfield.css +51 -0
- package/src/html/tokenfield.js +74 -0
- package/src/html/tree.css +63 -0
- package/src/html/tree.js +49 -0
- package/src/http/client.js +62 -0
- package/src/http/index.js +2 -0
- package/src/http/session.js +39 -0
- package/src/index.js +9 -0
- package/src/site/details.css +58 -0
- package/src/site/dialog.css +63 -0
- package/src/site/dialog.js +43 -0
- package/src/site/index.js +3 -0
- package/src/site/layouts.css +27 -0
- package/src/site/page.css +44 -0
- package/src/site/page.js +36 -0
- package/src/site/site.css +85 -0
- package/src/site/site.js +234 -0
- package/src/site/siteContext.js +4 -0
- package/src/site/workspace.js +57 -0
- package/src/upload/UploadArea.js +64 -0
- package/src/upload/UploadDialog.js +41 -0
- package/src/upload/UploadFile.js +31 -0
- package/src/upload/index.js +1 -0
- package/src/upload/uploader.css +57 -0
- package/src/upload/uploader.js +69 -0
- package/src/widgets/index.js +4 -0
- package/src/widgets/kanban/Kanban.css +80 -0
- package/src/widgets/kanban/Kanban.js +65 -0
- package/src/widgets/login/LoginBox.css +89 -0
- package/src/widgets/login/LoginBox.js +66 -0
- package/src/widgets/login/ResetPasswordBox.css +50 -0
- package/src/widgets/login/ResetPasswordBox.js +56 -0
- package/src/widgets/viewer/Viewer.css +87 -0
- package/src/widgets/viewer/Viewer.js +47 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
.checkbox {
|
2
|
+
flex: 1;
|
3
|
+
overflow: hidden;
|
4
|
+
display: flex;
|
5
|
+
align-items: center;
|
6
|
+
position: relative;
|
7
|
+
}
|
8
|
+
|
9
|
+
.checkbox .checkmark {
|
10
|
+
width: 1.5rem;
|
11
|
+
height: 1.4rem;
|
12
|
+
display: flex;
|
13
|
+
align-items: center;
|
14
|
+
justify-content: center;
|
15
|
+
margin: 0.3rem;
|
16
|
+
border: solid 1px var(--primary-color);
|
17
|
+
background-color: var(--paper-color);
|
18
|
+
padding-bottom: .1rem;
|
19
|
+
}
|
20
|
+
|
21
|
+
.checkbox .checkmark:after {
|
22
|
+
content: "";
|
23
|
+
width: 0.3rem;
|
24
|
+
height: 0.8rem;
|
25
|
+
border: solid var(--primary-color);
|
26
|
+
border-width: 0 3px 3px 0;
|
27
|
+
-webkit-transform: rotate(45deg);
|
28
|
+
-ms-transform: rotate(45deg);
|
29
|
+
transform: rotate(45deg);
|
30
|
+
display: none;
|
31
|
+
z-index: 0;
|
32
|
+
}
|
33
|
+
|
34
|
+
.checkbox > input {
|
35
|
+
position: absolute;
|
36
|
+
flex: 1;
|
37
|
+
width: 2rem;
|
38
|
+
height: 2rem;
|
39
|
+
opacity: 0;
|
40
|
+
z-index: 1;
|
41
|
+
}
|
42
|
+
|
43
|
+
input:checked ~ .checkmark:after {
|
44
|
+
display: block;
|
45
|
+
}
|
46
|
+
|
47
|
+
.checkbox > label {
|
48
|
+
color: var(--text-color-light);
|
49
|
+
font-size: 1rem;
|
50
|
+
font-weight: normal;
|
51
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import { Text } from './text'
|
3
|
+
import './checkbox.css'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* CheckBox
|
7
|
+
*/
|
8
|
+
export const CheckBox = (props) => {
|
9
|
+
|
10
|
+
const { id, label, placeholder, value, onChange } = props
|
11
|
+
|
12
|
+
function change(event) {
|
13
|
+
const value = event.target.checked
|
14
|
+
if (onChange) onChange(id, value)
|
15
|
+
}
|
16
|
+
|
17
|
+
const labelTxt = <Text>{label}</Text>
|
18
|
+
|
19
|
+
console.log(label, value)
|
20
|
+
return value === true ? (
|
21
|
+
<div className="checkbox" key={`${id}1`}>
|
22
|
+
<input id={id} type="checkbox" placeholder={placeholder} checked value={value} onChange={change} />
|
23
|
+
<span class="checkmark" />
|
24
|
+
<label htmlFor={id}>{labelTxt}</label>
|
25
|
+
</div>
|
26
|
+
) : (
|
27
|
+
<div className="checkbox" key={`${id}0`}>
|
28
|
+
<input id={id} type="checkbox" placeholder={placeholder} value={value} onChange={change} />
|
29
|
+
<span class="checkmark" />
|
30
|
+
<label htmlFor={id}>{labelTxt}</label>
|
31
|
+
</div>
|
32
|
+
)
|
33
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
.chips {
|
2
|
+
flex: 1;
|
3
|
+
display: flex;
|
4
|
+
overflow: auto;
|
5
|
+
max-height: 2rem;
|
6
|
+
}
|
7
|
+
|
8
|
+
.chip {
|
9
|
+
display: flex;
|
10
|
+
align-items: center;
|
11
|
+
border-radius: 2rem;
|
12
|
+
font-size: .9rem;
|
13
|
+
color: var(--text-color-light);
|
14
|
+
background-color: rgb(232, 232, 232);
|
15
|
+
margin: .2rem;
|
16
|
+
cursor: pointer;
|
17
|
+
}
|
18
|
+
|
19
|
+
.chip:hover {
|
20
|
+
background-color: rgba(200,200,200,.5);
|
21
|
+
}
|
22
|
+
|
23
|
+
.chip.selected {
|
24
|
+
background-color: var(--primary-color-lighter);
|
25
|
+
}
|
26
|
+
|
27
|
+
.chip.outlined {
|
28
|
+
background-color: transparent;
|
29
|
+
border: solid 1px var(--divider-color);
|
30
|
+
}
|
31
|
+
|
32
|
+
.chip.outlined:hover {
|
33
|
+
background-color: rgba(200,200,200,.5);
|
34
|
+
}
|
35
|
+
|
36
|
+
.chip.outlined.selected {
|
37
|
+
background-color: var(--primary-color-lighter);
|
38
|
+
border: solid 1px var(--primary-color-light);
|
39
|
+
}
|
40
|
+
|
41
|
+
.chip>.icon {
|
42
|
+
border-radius: 2rem;
|
43
|
+
width: 1.5rem;
|
44
|
+
height: 1.5rem;
|
45
|
+
margin: .2rem;
|
46
|
+
}
|
47
|
+
|
48
|
+
.chip>main {
|
49
|
+
flex: 1;
|
50
|
+
padding: .3rem;
|
51
|
+
display: flex;
|
52
|
+
align-items: center;
|
53
|
+
white-space: nowrap;
|
54
|
+
}
|
55
|
+
|
56
|
+
.chip>.meta>.icon {
|
57
|
+
background-color: rgba(200,200,200,.8);
|
58
|
+
border-radius: 2rem;
|
59
|
+
width: 1.2rem;
|
60
|
+
height: 1.2rem;
|
61
|
+
margin: .2rem;
|
62
|
+
font-size: .8rem;
|
63
|
+
}
|
package/src/html/chip.js
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
import { Icon } from './icon';
|
2
|
+
import { Text } from './text'
|
3
|
+
import './chip.css'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Chip
|
7
|
+
*/
|
8
|
+
export const Chip = (props) => {
|
9
|
+
|
10
|
+
const { id, icon, label, action, outlined, selected, onSelect } = props
|
11
|
+
|
12
|
+
function select(event) {
|
13
|
+
event.stopPropagation();
|
14
|
+
event.preventDefault();
|
15
|
+
if (onSelect) onSelect(id)
|
16
|
+
}
|
17
|
+
|
18
|
+
let style = outlined ? "outlined" : "normal"
|
19
|
+
if (selected) style = `${style} selected`
|
20
|
+
return (
|
21
|
+
<div className={`chip ${style}`} onClick={select}>
|
22
|
+
{icon ? <Icon icon={icon} size="small" /> : null}
|
23
|
+
<main><Text>{label}</Text></main>
|
24
|
+
{action ? <span className="meta">{action}</span> : null}
|
25
|
+
</div>
|
26
|
+
)
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Chips
|
31
|
+
*/
|
32
|
+
export const Chips = (props) => {
|
33
|
+
const { children } = props
|
34
|
+
return (
|
35
|
+
<div className="chips">
|
36
|
+
{children}
|
37
|
+
</div>
|
38
|
+
)
|
39
|
+
}
|
package/src/html/form.js
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
import React, { Fragment, useState, useEffect } from 'react'
|
2
|
+
import { Text } from './text';
|
3
|
+
import './form.css'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Form
|
7
|
+
*/
|
8
|
+
export const Form = ({ title, columns = 1, children, outlined, onChange }) => {
|
9
|
+
|
10
|
+
const [fields, setFields] = useState([])
|
11
|
+
const isEmpty = (value) => value === void 0 || value === null || value === '';
|
12
|
+
|
13
|
+
useEffect(() => {
|
14
|
+
const initFields = React.Children
|
15
|
+
.toArray(children)
|
16
|
+
.filter(child => child !== null && child !== '')
|
17
|
+
.map(child => {
|
18
|
+
const { id, value, required = false, validation } = child.props
|
19
|
+
const valid = required ? validation ? validation(value) : !isEmpty(value) : true
|
20
|
+
return { id, value, required, validation, valid }
|
21
|
+
})
|
22
|
+
.filter(field => field.id !== void 0)
|
23
|
+
setFields(initFields)
|
24
|
+
}, [])
|
25
|
+
|
26
|
+
useEffect(() => {
|
27
|
+
if (onChange) {
|
28
|
+
const valid = fields.every(({ valid }) => valid === true)
|
29
|
+
const form = fields.reduce((form, { id, value }) => {
|
30
|
+
if (value || value === false) form[id] = value
|
31
|
+
return form
|
32
|
+
}, {})
|
33
|
+
onChange(form, valid)
|
34
|
+
}
|
35
|
+
}, [fields])
|
36
|
+
|
37
|
+
const changeField = (id, value) => {
|
38
|
+
const field = fields.find(f => f.id === id)
|
39
|
+
if (field) {
|
40
|
+
const valid = field.required ? field.validation ? field.validation(value) : !isEmpty(value) : true
|
41
|
+
Object.assign(field, { value, valid })
|
42
|
+
setFields(fields.slice())
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
const items = React.Children
|
47
|
+
.toArray(children)
|
48
|
+
.filter(child => child !== null && child !== '')
|
49
|
+
.map(child => {
|
50
|
+
const { span = 1 } = child.props
|
51
|
+
const field = React.cloneElement(child, {
|
52
|
+
onChange: changeField,
|
53
|
+
outlined: !!outlined
|
54
|
+
})
|
55
|
+
const columnLayout = { gridColumn: `span ${span}`}
|
56
|
+
return (<FieldWrapper style={columnLayout}>{field}</FieldWrapper>)
|
57
|
+
})
|
58
|
+
|
59
|
+
|
60
|
+
const gridLayout = { gridTemplateColumns: `repeat(${columns}, 1fr)` }
|
61
|
+
return (
|
62
|
+
<Fragment>
|
63
|
+
{title ? (<header className="form-header"><Text use="headline6">{title}</Text></header>) : ''}
|
64
|
+
<form className="form-grid" style={gridLayout}>
|
65
|
+
{items}
|
66
|
+
</form>
|
67
|
+
</Fragment>
|
68
|
+
)
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Form Field Wrapper
|
73
|
+
*/
|
74
|
+
const FieldWrapper = ({ style, children }) => {
|
75
|
+
return (
|
76
|
+
<div className="field-wrapper" style={style}>
|
77
|
+
{children}
|
78
|
+
</div>
|
79
|
+
)
|
80
|
+
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
.header {
|
2
|
+
display: flex;
|
3
|
+
padding: 0;
|
4
|
+
min-height: 3rem;
|
5
|
+
align-items: center;
|
6
|
+
background-repeat: no-repeat;
|
7
|
+
background-size: cover;
|
8
|
+
}
|
9
|
+
|
10
|
+
.header>.icon .header>img {
|
11
|
+
margin-right: 1.6rem
|
12
|
+
}
|
13
|
+
|
14
|
+
.header>label {
|
15
|
+
margin-left: 1rem
|
16
|
+
}
|
17
|
+
|
18
|
+
.header.caption {
|
19
|
+
height: 4rem;
|
20
|
+
border-bottom: solid 1px var(--divider-color);
|
21
|
+
}
|
22
|
+
|
23
|
+
.header.dense {
|
24
|
+
height: 2.4rem;
|
25
|
+
padding: 0;
|
26
|
+
}
|
27
|
+
|
28
|
+
.header.prominent {
|
29
|
+
height: 12.8rem;
|
30
|
+
align-items: flex-start;
|
31
|
+
}
|
32
|
+
|
33
|
+
.header.prominent>label {
|
34
|
+
align-self: flex-end;
|
35
|
+
}
|
36
|
+
|
37
|
+
.header-icon {
|
38
|
+
max-width: 4rem;
|
39
|
+
max-height: 4rem;
|
40
|
+
object-fit: contain;
|
41
|
+
padding: .5rem;
|
42
|
+
display: flex;
|
43
|
+
justify-content: center;
|
44
|
+
align-items: center;
|
45
|
+
}
|
46
|
+
|
47
|
+
.header>.actions {
|
48
|
+
position: relative;
|
49
|
+
flex: 1;
|
50
|
+
display: flex;
|
51
|
+
flex-direction: row-reverse;
|
52
|
+
align-items: center;
|
53
|
+
overflow: vivsible;
|
54
|
+
}
|
55
|
+
|
56
|
+
.header.primary {
|
57
|
+
background-color: var(--primary-color-dark);
|
58
|
+
color: var(--primary-color-text);
|
59
|
+
}
|
60
|
+
|
61
|
+
.header.secondary {
|
62
|
+
color: var(--secondary-color-text);
|
63
|
+
background-color: var(--secondary-color-dark);
|
64
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Icon } from './icon';
|
3
|
+
import { Text } from './text';
|
4
|
+
import './header.css';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Header
|
8
|
+
*/
|
9
|
+
export const Header = (props) => {
|
10
|
+
|
11
|
+
const caption = props.caption ? 'caption' : ''
|
12
|
+
const prominent = props.prominent ? 'prominent' : ''
|
13
|
+
const dense = props.dense ? 'dense' : ''
|
14
|
+
let theme = props.primary ? 'primary' : ''
|
15
|
+
theme = props.secondary ? 'secondary' : theme
|
16
|
+
let icon = props.icon ? <Icon icon={props.icon} clickable={props.clickable} action={props.action}/> : ''
|
17
|
+
icon = props.iconSrc ? <img className="header-icon" src={props.iconSrc} /> : icon;
|
18
|
+
|
19
|
+
const style = props.img ? { backgroundImage: `url(${props.img })` } : {}
|
20
|
+
|
21
|
+
const title=<Text>{props.title}</Text>
|
22
|
+
return (
|
23
|
+
<header className={`header ${caption} ${prominent} ${dense} ${theme} ${props.className}`} style={style}>
|
24
|
+
{icon}
|
25
|
+
{props.title ? <label>{title}</label> : null }
|
26
|
+
<span className="actions">{props.children}</span>
|
27
|
+
</header>
|
28
|
+
)
|
29
|
+
}
|
30
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
.icon {
|
2
|
+
position: relative;
|
3
|
+
display: flex !important;
|
4
|
+
align-items: center;
|
5
|
+
justify-content: center;
|
6
|
+
}
|
7
|
+
|
8
|
+
.icon:before {
|
9
|
+
content: "";
|
10
|
+
position: absolute;
|
11
|
+
top: 50%;
|
12
|
+
left: 50%;
|
13
|
+
display: block;
|
14
|
+
width: 0;
|
15
|
+
padding-top: 0;
|
16
|
+
border-radius: 100%;
|
17
|
+
background-color: rgba(236, 240, 241, 0.548);
|
18
|
+
-webkit-transform: translate(-50%, -50%);
|
19
|
+
-moz-transform: translate(-50%, -50%);
|
20
|
+
-ms-transform: translate(-50%, -50%);
|
21
|
+
-o-transform: translate(-50%, -50%);
|
22
|
+
transform: translate(-50%, -50%);
|
23
|
+
}
|
24
|
+
|
25
|
+
.icon:active:before {
|
26
|
+
width: 120%;
|
27
|
+
padding-top: 120%;
|
28
|
+
transition: width .2s ease-out, padding-top .2s ease-out;
|
29
|
+
}
|
30
|
+
|
31
|
+
.icon.clickable:hover {
|
32
|
+
background-color: rgba(100,100,100,.1);
|
33
|
+
cursor: pointer;
|
34
|
+
border-radius: 100%;
|
35
|
+
}
|
36
|
+
|
37
|
+
.icon.small {
|
38
|
+
width: 2rem;
|
39
|
+
height: 2rem;
|
40
|
+
font-size: 1.2rem;
|
41
|
+
}
|
42
|
+
|
43
|
+
.icon.normal {
|
44
|
+
width: 3rem;
|
45
|
+
height: 3rem;
|
46
|
+
font-size: 1.7rem;
|
47
|
+
}
|
48
|
+
|
49
|
+
.icon.large {
|
50
|
+
width: 4rem;
|
51
|
+
height: 4rem;
|
52
|
+
font-size: 2rem;
|
53
|
+
}
|
package/src/html/icon.js
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import './icon.css'
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Icon
|
6
|
+
*/
|
7
|
+
export const Icon = ({ icon, size = "normal", clickable = false, action }) => {
|
8
|
+
|
9
|
+
function click (event) {
|
10
|
+
event.stopPropagation()
|
11
|
+
event.preventDefault();
|
12
|
+
if (action) action()
|
13
|
+
}
|
14
|
+
|
15
|
+
const clicker = clickable ? "clickable" : ""
|
16
|
+
return (
|
17
|
+
<i className={`icon ${size} ${clicker} material-icons`} onClick={click}>
|
18
|
+
{icon}
|
19
|
+
</i>
|
20
|
+
)
|
21
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
export { Button } from './button'
|
2
|
+
export { CheckBox } from './checkbox'
|
3
|
+
export { Chips, Chip } from './chip'
|
4
|
+
export { Form } from './form'
|
5
|
+
export { Header } from './header'
|
6
|
+
export { Icon } from './icon'
|
7
|
+
export { List } from './list'
|
8
|
+
export { MenuIcon, Menu, MenuItem, MenuSeparator } from './menu'
|
9
|
+
export { CircularProgress, LinearProgress } from './progress'
|
10
|
+
export { Property } from './property'
|
11
|
+
export { RadioButton } from './radio'
|
12
|
+
export { Section } from './section'
|
13
|
+
export { Tabs, Tab, Stack } from './tab'
|
14
|
+
export { DataTable } from './table'
|
15
|
+
export { Text } from './text'
|
16
|
+
export { TextField, DropDown } from './textfield'
|
17
|
+
export { TokenField } from './tokenfield'
|
18
|
+
export { Tree, TreeNode, TreeItem } from './tree'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
.list {
|
2
|
+
flex: 1;
|
3
|
+
overflow: hidden;
|
4
|
+
display: flex;
|
5
|
+
flex-direction: column;
|
6
|
+
}
|
7
|
+
|
8
|
+
.list>header {
|
9
|
+
padding: .5rem 0 0 1.5rem;
|
10
|
+
color: var(--text-color-light);
|
11
|
+
text-transform: capitalize;
|
12
|
+
font-size: .8rem;
|
13
|
+
}
|
14
|
+
|
15
|
+
.list > ul {
|
16
|
+
flex: 1;
|
17
|
+
overflow: auto;
|
18
|
+
list-style: none;
|
19
|
+
margin: 0;
|
20
|
+
padding: 0.5rem 0;
|
21
|
+
cursor: pointer;
|
22
|
+
display: flex;
|
23
|
+
flex-direction: column;
|
24
|
+
}
|
25
|
+
|
26
|
+
.list > ul li {
|
27
|
+
flex: 1;
|
28
|
+
overflow: hidden;
|
29
|
+
min-height: 3rem;
|
30
|
+
padding: .3rem 0.5rem;
|
31
|
+
display: flex;
|
32
|
+
align-items: center;
|
33
|
+
}
|
34
|
+
|
35
|
+
.list > ul li:hover {
|
36
|
+
background-color: var(--primary-color-lighter);
|
37
|
+
}
|
38
|
+
|
39
|
+
.list > ul li > .icon {
|
40
|
+
color: var(--text-color-lighter);
|
41
|
+
}
|
42
|
+
|
43
|
+
.list > ul li > main {
|
44
|
+
flex: 1;
|
45
|
+
overflow: hidden;
|
46
|
+
display: flex;
|
47
|
+
flex-direction: column;
|
48
|
+
}
|
49
|
+
|
50
|
+
.list > ul li > main > * {
|
51
|
+
flex: 1;
|
52
|
+
padding: 0 1rem;
|
53
|
+
display: flex;
|
54
|
+
align-items: flex-end;
|
55
|
+
overflow: hidden;
|
56
|
+
white-space: nowrap;
|
57
|
+
text-overflow: ellipsis;
|
58
|
+
}
|
59
|
+
|
60
|
+
.list > ul li > main > .primary-line {
|
61
|
+
font-size: 1rem;
|
62
|
+
color: var(--text-color);
|
63
|
+
}
|
64
|
+
|
65
|
+
.list > ul li > main > .secondary-line {
|
66
|
+
font-size: .9rem;
|
67
|
+
color: var(--text-color-lighter);
|
68
|
+
}
|
69
|
+
|
70
|
+
.list > ul li > .meta {
|
71
|
+
|
72
|
+
}
|
package/src/html/list.js
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
import { Fragment } from 'react'
|
2
|
+
import { Icon } from './icon';
|
3
|
+
import { Text } from './text';
|
4
|
+
import "./list.css"
|
5
|
+
|
6
|
+
/**
|
7
|
+
* List
|
8
|
+
*/
|
9
|
+
export const List = (props) => {
|
10
|
+
|
11
|
+
const { items = [], children, onSelect, groupBy } = props
|
12
|
+
|
13
|
+
function select(id) {
|
14
|
+
if (onSelect) onSelect(id)
|
15
|
+
}
|
16
|
+
|
17
|
+
return groupBy ? <GroupedList {...props} onSelect={select} /> : (
|
18
|
+
<div className="list">
|
19
|
+
<ul>
|
20
|
+
{items.map(item => <ListItem key={item.id} item={item} onSelect={select} />)}
|
21
|
+
</ul>
|
22
|
+
{children}
|
23
|
+
</div>
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Grouped List
|
29
|
+
*/
|
30
|
+
const GroupedList = (props) => {
|
31
|
+
const { items = [], children, onSelect, groupBy } = props
|
32
|
+
|
33
|
+
const groups = items.reduce((groups, item) => {
|
34
|
+
let group = groups.find(g => g.name === item.content[groupBy])
|
35
|
+
if (!group) {
|
36
|
+
group = { name: item.content[groupBy], items: []}
|
37
|
+
groups.push(group)
|
38
|
+
}
|
39
|
+
group.items.push(item)
|
40
|
+
return groups
|
41
|
+
}, [])
|
42
|
+
|
43
|
+
return (
|
44
|
+
<div className="list">
|
45
|
+
{groups.map(group => (
|
46
|
+
<Fragment key={group.name}>
|
47
|
+
<header key={`${group.name}-header`}><Text>{group.name}</Text></header>
|
48
|
+
<ul key={`${group.name}-ul`}>
|
49
|
+
{group.items.map(item => <ListItem key={item.id} item={item} onSelect={onSelect} />)}
|
50
|
+
</ul>
|
51
|
+
</Fragment>
|
52
|
+
))}
|
53
|
+
{children}
|
54
|
+
</div>
|
55
|
+
)
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* List Item
|
60
|
+
*/
|
61
|
+
const ListItem = ({ item, onSelect }) => {
|
62
|
+
const { id, icon, line1, line2, meta } = item
|
63
|
+
|
64
|
+
function select() {
|
65
|
+
if (onSelect) onSelect(id)
|
66
|
+
}
|
67
|
+
|
68
|
+
return (
|
69
|
+
<li id={id} onClick={select}>
|
70
|
+
{icon ? <Icon icon={icon} size="small" /> : null}
|
71
|
+
<main>
|
72
|
+
<div className="primary-line"><Text>{line1}</Text></div>
|
73
|
+
{line2 ? <div className="secondary-line"><Text>{line2}</Text></div> : null}
|
74
|
+
</main>
|
75
|
+
{meta ? <div className="meta">{meta}</div> : null}
|
76
|
+
</li>
|
77
|
+
)
|
78
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
.menu-icon {
|
2
|
+
position: relative;
|
3
|
+
width: 3rem;
|
4
|
+
}
|
5
|
+
|
6
|
+
.menu-icon>.overlay {
|
7
|
+
position: fixed;
|
8
|
+
top: 0px;
|
9
|
+
left: 0px;
|
10
|
+
width: 100vw;
|
11
|
+
height: 100vh;
|
12
|
+
z-index: 5;
|
13
|
+
background-color: transparent;
|
14
|
+
}
|
15
|
+
|
16
|
+
.menu-icon>menu {
|
17
|
+
z-index: 6;
|
18
|
+
position: absolute;
|
19
|
+
top: 3rem;
|
20
|
+
left: 0px;
|
21
|
+
margin: 0;
|
22
|
+
border: solid 1px var(--divider-color);
|
23
|
+
background-color: var(--paper-color);
|
24
|
+
padding: 0;
|
25
|
+
min-width: 7rem;
|
26
|
+
max-width: 14rem;
|
27
|
+
max-height: 50vh;
|
28
|
+
overflow: auto;
|
29
|
+
box-shadow: var(--shadow1);
|
30
|
+
}
|
31
|
+
|
32
|
+
.menu-icon>menu.alignRight {
|
33
|
+
left:unset;
|
34
|
+
right: 0px;
|
35
|
+
}
|
36
|
+
|
37
|
+
.menu-icon>menu ul {
|
38
|
+
list-style: none;
|
39
|
+
margin: 0;
|
40
|
+
padding: .5rem 0;
|
41
|
+
cursor: pointer;
|
42
|
+
}
|
43
|
+
|
44
|
+
.menu-icon>menu li {
|
45
|
+
min-height: 3rem;
|
46
|
+
padding: 0 .5rem;
|
47
|
+
|
48
|
+
display: flex;
|
49
|
+
align-items: center;
|
50
|
+
}
|
51
|
+
|
52
|
+
.menu-icon>menu li:hover {
|
53
|
+
background-color: var(--primary-color-lighter);
|
54
|
+
}
|
55
|
+
|
56
|
+
.menu-item {
|
57
|
+
flex: 1;
|
58
|
+
display: flex;
|
59
|
+
align-items: center;
|
60
|
+
}
|
61
|
+
|
62
|
+
.menu-item>.icon {
|
63
|
+
color: var(--text-color-light)
|
64
|
+
}
|
65
|
+
|
66
|
+
.menu-item>label {
|
67
|
+
flex: 1;
|
68
|
+
white-space: nowrap;
|
69
|
+
padding: 0 .5rem 0 0;
|
70
|
+
color: var(--text-color);
|
71
|
+
font-size: .9rem;
|
72
|
+
}
|
73
|
+
|
74
|
+
.menu-item>.meta {
|
75
|
+
color: var(--text-color-lighter)
|
76
|
+
}
|