gristlabs_grist-core/app/client/ui/AdminPanelCss.ts

195 lines
4.5 KiB
TypeScript
Raw Normal View History

import {transition} from 'app/client/ui/transitions';
import {toggle} from 'app/client/ui2018/checkbox';
import {mediaSmall, testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons';
import {dom, DomContents, IDisposableOwner, Observable, styled} from 'grainjs';
export function HidableToggle(owner: IDisposableOwner, value: Observable<boolean|null>) {
return toggle(value, dom.hide((use) => use(value) === null));
}
export function AdminSection(owner: IDisposableOwner, title: DomContents, items: DomContents[]) {
return cssSection(
cssSectionTitle(title),
...items,
);
}
export function AdminSectionItem(owner: IDisposableOwner, options: {
id: string,
name?: DomContents,
description?: DomContents,
value?: DomContents,
expandedContent?: DomContents,
}) {
const itemContent = (...prefix: DomContents[]) => [
cssItemName(
...prefix,
options.name,
testId(`admin-panel-item-name-${options.id}`),
prefix.length ? cssItemName.cls('-prefixed') : null,
),
cssItemDescription(options.description),
cssItemValue(options.value,
testId(`admin-panel-item-value-${options.id}`),
dom.on('click', ev => ev.stopPropagation())),
];
if (options.expandedContent) {
const isCollapsed = Observable.create(owner, true);
return cssItem(
cssItemShort(
itemContent(dom.domComputed(isCollapsed, (c) => cssCollapseIcon(c ? 'Expand' : 'Collapse'))),
cssItemShort.cls('-expandable'),
dom.on('click', () => isCollapsed.set(!isCollapsed.get())),
),
cssExpandedContentWrap(
transition(isCollapsed, {
prepare(elem, close) { elem.style.maxHeight = close ? elem.scrollHeight + 'px' : '0'; },
run(elem, close) { elem.style.maxHeight = close ? '0' : elem.scrollHeight + 'px'; },
finish(elem, close) { elem.style.maxHeight = close ? '0' : 'unset'; },
}),
cssExpandedContent(
options.expandedContent,
),
),
testId(`admin-panel-item-${options.id}`),
);
} else {
return cssItem(
cssItemShort(itemContent()),
testId(`admin-panel-item-${options.id}`),
);
}
}
const cssSection = styled('div', `
padding: 24px;
max-width: 600px;
width: 100%;
margin: 16px auto;
border: 1px solid ${theme.widgetBorder};
border-radius: 4px;
& > div + div {
margin-top: 8px;
}
@media ${mediaSmall} {
& {
width: auto;
padding: 12px;
margin: 8px;
}
}
`);
const cssSectionTitle = styled('div', `
height: 32px;
line-height: 32px;
margin-bottom: 8px;
font-size: ${vars.headerControlFontSize};
font-weight: ${vars.headerControlTextWeight};
`);
const cssItem = styled('div', `
margin-top: 8px;
container-type: inline-size;
container-name: line;
`);
const cssItemShort = styled('div', `
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 8px;
margin: 0 -8px;
border-radius: 4px;
&-expandable {
cursor: pointer;
}
&-expandable:hover {
background-color: ${theme.lightHover};
}
@container line (max-width: 500px) {
& {
flex-direction: column;
align-items: stretch;
gap: 8px;
}
}
`);
const cssItemName = styled('div', `
width: 136px;
font-weight: bold;
display: flex;
align-items: center;
font-size: ${vars.largeFontSize};
padding-left: 24px;
&-prefixed {
padding-left: 0;
}
@container line (max-width: 500px) {
& {
padding-left: 0;
}
}
@media ${mediaSmall} {
& {
width: calc(100% - 28px);
padding-left: 0;
}
&:first-child {
margin-left: 0;
}
}
`);
const cssItemDescription = styled('div', `
margin-right: auto;
`);
const cssItemValue = styled('div', `
flex: none;
margin: -16px;
padding: 16px;
cursor: auto;
`);
const cssCollapseIcon = styled(icon, `
width: 24px;
height: 24px;
margin-right: 4px;
margin-left: -4px;
--icon-color: ${theme.lightText};
`);
const cssExpandedContentWrap = styled('div', `
transition: max-height 0.3s ease-in-out;
overflow: hidden;
max-height: 0;
`);
const cssExpandedContent = styled('div', `
margin-left: 24px;
padding: 24px 0;
border-bottom: 1px solid ${theme.widgetBorder};
.${cssItem.className}:last-child & {
padding-bottom: 0;
border-bottom: none;
}
@container line (max-width: 500px) {
& {
margin-left: 0px;
}
}
`);
export const cssValueLabel = styled('div', `
padding: 4px 8px;
color: ${theme.text};
border: 1px solid ${theme.inputBorder};
border-radius: ${vars.controlBorderRadius};
`);