sober
Version:
Sober is a library of UI components designed with reference to Material You.
222 lines (221 loc) • 5.83 kB
JavaScript
import { useElement, useProps } from './core/element.js';
import './ripple.js';
const name = 's-field';
const props = useProps({
focused: false,
fixed: true
});
const style = /*css*/ `
:host{
display: inline-block;
vertical-align: middle;
font-size: .875rem;
--field-padding: 16px;
--field-padding-top: var(--field-padding);
--field-padding-bottom: var(--field-padding);
--field-padding-left: var(--field-padding);
--field-padding-right: var(--field-padding);
--field-border-radius: 4px;
--field-border-width: 1px;
--field-focused-border-width: 2px;
--field-border-color: var(--s-color-outline, ${"#70787D" /* Theme.colorOutline */});
}
.container{
display: flex;
height: 100%;
min-height: inherit;
}
.line{
position: relative;
}
.line::before,
.line::after{
position: absolute;
content: '';
top: 0;
left: 0;
height: 100%;
width: 100%;
box-sizing: border-box;
border-width: var(--field-border-width);
border-color: var(--field-border-color);
pointer-events: none;
}
.line::after{
border-width: var(--field-focused-border-width);
border-color: var(--s-color-primary, ${"#006782" /* Theme.colorPrimary */});
opacity: 0;
transition: opacity .2s ease-out;
}
:host([focused=true]) .line::after{
opacity: 1;
}
.start,
.end{
display: flex;
align-items: center;
flex-shrink: 0;
min-width: var(--field-border-radius);
}
.start::before,
.end::before,
.start::after,
.end::after{
border-top-style: solid;
border-bottom-style: solid;
}
.start::before,
.start::after{
border-left-style: solid;
border-top-left-radius: var(--field-border-radius);
border-bottom-left-radius: var(--field-border-radius);
}
.end::before,
.end::after{
border-right-style: solid;
border-top-right-radius: var(--field-border-radius);
border-bottom-right-radius: var(--field-border-radius);
left: auto;
right: 0;
}
.box{
display: grid;
grid-template-areas: "a" "a";
flex-grow: 1;
}
.box::before,
.box::after{
border-bottom-style: solid;
}
.top,
.view{
height: 100%;
display: flex;
grid-area: a;
position: relative;
box-sizing: border-box;
margin-left: calc(var(--field-border-radius) * -1);
margin-right: calc(var(--field-border-radius) * -1);
}
.view{
align-items: center;
}
.top{
pointer-events: none;
}
.top>.left::before,
.top>.right::before,
.top>.left::after,
.top>.right::after{
border-top-style: solid;
}
.top>.left{
width: calc(var(--field-padding-left) - var(--field-border-radius) - 4px);
margin-left: max(4px, var(--field-border-radius));
margin-right: min(4px, calc(var(--field-padding-left) - var(--field-border-radius)));
flex-shrink: 0;
}
:host([fixed=false]) .top>.left{
width: calc(var(--field-padding-left) - var(--field-border-radius));
margin-right: 0;
}
.top>.right{
flex-grow: 1;
width: calc(var(--field-padding-right) - var(--field-border-radius) - 4px);
margin-left: min(4px, calc(var(--field-padding-right) - var(--field-border-radius)));
margin-right: max(4px, var(--field-border-radius));
}
:host([fixed=false]) .top>.right{
width: calc(var(--field-padding-left) - var(--field-border-radius));
margin-left: 0;
}
.label{
display: block;
height: 100%;
flex-shrink: 0;
}
.label>.line::before,
.label>.line::after,
:host([fixed=false]) .label::before,
:host([fixed=false]) .label::after{
border-top-style: solid;
}
.label>.line::before,
.label>.line::after{
transform: translateX(-50%);
width: min(calc(var(--field-padding-right) - var(--field-border-radius)), 8px);
}
::slotted([slot=label]){
display: flex;
align-items: center;
height: 100%;
font-size: .75rem;
align-items: center;
transform: translateY(-50%);
color: var(--field-border-color);
transition: transform .2s ease-out, font-size .2s ease-out;
box-sizing: border-box;
position: relative;
}
:host([fixed=false]) ::slotted([slot=label]),
::slotted([slot=label]:empty){
font-size: inherit;
transform: translateY(0);
}
::slotted([slot=label]:empty)::before,
::slotted([slot=label]:empty)::after{
content: '';
position: absolute;
top: 0;
left: 0;
transform: translateX(-50%);
width: min(calc(var(--field-padding-right) - var(--field-border-radius)), 8px);
border-top: solid var(--field-border-width); var(--field-border-color);
}
::slotted([slot=label]:empty)::after{
opacity: 0;
border-width: var(--field-focused-border-width);
transition: opacity .2s ease-out;
border-color: var(--s-color-primary, ${"#006782" /* Theme.colorPrimary */});
}
:host([focused=true]) ::slotted([slot=label]:empty)::after{
opacity: 1;
}
:host([focused=true]) ::slotted([slot=label]){
color: var(--s-color-primary, ${"#006782" /* Theme.colorPrimary */});
}
::slotted(:not([slot])){
padding-left: var(--field-padding-left);
padding-right: var(--field-padding-right);
padding-top: var(--field-padding-top);
padding-bottom: var(--field-padding-bottom);
display: flex;
align-items: center;
height: 100%;
box-sizing: border-box;
}
`;
const template = /*html*/ `
<div class="container" part="container">
<div class="start line" part="start">
<slot name="start"></slot>
</div>
<div class="box line" part="box">
<slot class="view"></slot>
<div class="top" part="top">
<div class="line left"></div>
<slot name="label" class="label line">
<div class="line"></div>
</slot>
<div class="line right"></div>
</div>
</div>
<div class="end line" part="end">
<slot name="end"></slot>
</div>
<slot name="custom"></slot>
</div>
`;
export class Field extends useElement({ style, template, props }) {
}
Field.define(name);