lila/ui/common/css/component/_tagify.scss

749 lines
17 KiB
SCSS

:root {
// should be same as "$tags-focus-border-color"
--tagify-dd-color-primary: #{$c-primary};
--tagify-dd-bg-color: #{$c-bg-box};
}
.tagify {
// SCSS "default" allows overriding variables BEFORE they are set in the below lines of code
$self: &;
$tags-border-color: $c-border !default;
$tags-hover-border-color: c-clearer($c-border) !default;
$tags-focus-border-color: $c-primary !default;
$tagMargin: 5px !default;
$tag-pad: 0.3em 0.5em !default;
$tag-min-width: 1ch !default;
$tag-max-width: auto !default;
$tag-text-color: $c-font-clearer !default;
$tag-text-color--edit: $c-font-clearer !default;
$tag-bg: $c-bg-zebra2 !default;
$tag-hover: mix($c-primary, $c-bg-zebra2, 20%) !default;
$tag-remove: mix($c-bad, $c-bg-zebra2, 60%) !default;
$tag-remove-btn-color: $tag-text-color !default;
$tag-remove-btn-bg: none !default;
$tag-remove-btn-bg--hover: $c-bad !default;
$tag-invalid-color: $tag-remove !default;
$tag-invalid-bg: mix($c-bad, $c-bg-zebra2, 20%) !default;
$tag-inset-shadow-size: 1.5em !default;
$tag-hide-transition: 0.3s !default;
$placeholder-color: rgba($tag-text-color, 0.4) !default;
$placeholder-color-focus: rgba($tag-text-color, 0.25) !default;
$input-color: inherit !default;
$tagify-dd-bg-color: white !default;
$tagify-dd-color-primary: $c-primary !default;
// CSS variables
--tags-border-color: #{$tags-border-color};
--tags-hover-border-color: #{$tags-hover-border-color};
--tags-focus-border-color: #{$tags-focus-border-color};
--tag-bg: #{$tag-bg};
--tag-hover: #{$tag-hover};
--tag-text-color: #{$tag-text-color};
--tag-text-color--edit: #{$tag-text-color--edit};
--tag-pad: #{$tag-pad};
--tag-inset-shadow-size: #{$tag-inset-shadow-size};
--tag-invalid-color: #{$tag-invalid-color};
--tag-invalid-bg: #{$tag-invalid-bg};
--tag-remove-bg: #{rgba($tag-remove, 0.3)};
--tag-remove-btn-color: #{$tag-remove-btn-color};
--tag-remove-btn-bg: #{$tag-remove-btn-bg};
--tag-remove-btn-bg--hover: #{$tag-remove-btn-bg--hover};
--input-color: #{$input-color};
--tag--min-width: #{$tag-min-width};
--tag--max-width: #{$tag-max-width};
--tag-hide-transition: #{$tag-hide-transition};
--placeholder-color: #{$placeholder-color};
--placeholder-color-focus: #{$placeholder-color-focus};
--loader-size: 0.8em;
@mixin firefox {
@at-root {
@-moz-document url-prefix() {
& {
@content;
}
}
}
}
@mixin placeholder($show: true, $opacity: 0.5) {
transition: 0.2s ease-out;
@if $show == true {
opacity: $opacity;
transform: none;
} @else {
opacity: 0;
transform: translatex(6px);
}
}
@mixin loader() {
content: '';
vertical-align: middle;
margin: -2px 0 -2px 0.5em;
opacity: 1;
width: 0.7em;
height: 0.7em;
width: var(--loader-size);
height: var(--loader-size);
border: 3px solid;
border-color: #eee #bbb #888 transparent;
border-radius: 50%;
animation: rotateLoader 0.4s infinite linear;
}
@mixin tagReadonlyBG {
background: linear-gradient(
45deg,
var(--tag-bg) 25%,
transparent 25%,
transparent 50%,
var(--tag-bg) 50%,
var(--tag-bg) 75%,
transparent 75%,
transparent
)
0 / 5px 5px;
box-shadow: none;
filter: brightness(0.95);
}
@keyframes tags--bump {
30% {
transform: scale(1.2);
}
}
@keyframes rotateLoader {
to {
transform: rotate(1turn);
}
}
@extend %box-radius;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
border: 1px solid $tags-border-color;
border: 1px solid var(--tags-border-color);
padding: 0;
margin: 1em 0;
line-height: normal;
cursor: text;
outline: none;
position: relative;
box-sizing: border-box;
transition: 0.1s;
text-align: left;
height: auto;
&:hover {
border-color: $tags-hover-border-color;
border-color: var(--tags-hover-border-color);
}
&.tagify--focus {
transition: 0s;
border-color: $tags-focus-border-color;
border-color: var(--tags-focus-border-color);
}
// Global "read-only" mode (no input button)
&[readonly] {
&:not(.tagify--mix) {
cursor: default;
> #{$self}__input {
visibility: hidden;
width: 0;
margin: $tagMargin 0;
}
#{$self}__tag__removeBtn {
display: none;
}
#{$self}__tag > div {
padding: $tag-pad;
padding: var(--tag-pad);
&::before {
@include tagReadonlyBG;
}
}
}
}
&--loading {
#{$self}__input {
> br:last-child {
display: none;
}
&::before {
content: none;
}
&::after {
@include loader;
content: '' !important;
margin: -2px 0 -2px 0.5em;
}
&:empty {
&::after {
margin-left: 0;
}
}
}
}
///////////////////////////////////////////
// Hides originals
+ input,
+ textarea {
position: absolute !important;
left: -9999em !important;
transform: scale(0) !important;
}
&__tag {
display: inline-flex;
align-items: center;
margin: $tagMargin 0 $tagMargin $tagMargin;
position: relative;
z-index: 1;
outline: none;
cursor: default;
transition: 0.13s ease-out;
> div {
vertical-align: top;
box-sizing: border-box;
max-width: 100%;
padding: $tag-pad;
padding: var(--tag-pad, $tag-pad);
color: $tag-text-color;
color: var(--tag-text-color, $tag-text-color);
line-height: inherit;
border-radius: 3px;
white-space: nowrap;
transition: 0.13s ease-out;
> * {
white-space: pre-wrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: top;
min-width: $tag-min-width;
max-width: $tag-max-width;
min-width: var(--tag--min-width, $tag-min-width);
max-width: var(--tag--max-width, $tag-max-width);
transition: 0.8s ease, 0.1s color;
&[contenteditable] {
outline: none;
user-select: text;
cursor: text;
// fix: sometimes the caret after the last character wasn't visible (when setting {backspace:"edit"})
margin: -2px;
padding: 2px;
max-width: 350px;
}
}
&::before {
content: '';
position: absolute;
border-radius: inherit;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: -1;
pointer-events: none;
transition: 120ms ease;
animation: tags--bump 0.3s ease-out 1;
box-shadow: 0 0 0 $tag-inset-shadow-size $tag-bg inset;
box-shadow: 0 0 0 var(--tag-inset-shadow-size, $tag-inset-shadow-size) var(--tag-bg, $tag-bg) inset;
}
}
&:hover:not([readonly]) {
div {
&::before {
$size: -$tagMargin / 2;
$size: -2px;
top: $size;
right: $size;
bottom: $size;
left: $size;
box-shadow: 0 0 0 $tag-inset-shadow-size $tag-hover inset;
box-shadow: 0 0 0 var(--tag-inset-shadow-size, $tag-inset-shadow-size) var(--tag-hover, $tag-hover) inset;
}
}
}
&--loading {
pointer-events: none;
.tagify__tag__removeBtn {
display: none;
}
&::after {
--loader-size: 0.4em;
@include loader;
margin: 0 0.5em 0 -0.1em;
}
}
&--flash {
div::before {
animation: none;
}
}
&--hide {
width: 0 !important;
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0;
opacity: 0;
transform: scale(0);
transition: $tag-hide-transition;
transition: var(--tag-hide-transition, $tag-hide-transition);
pointer-events: none;
> div > * {
white-space: nowrap;
}
}
&#{$self} {
&--noAnim {
> div::before {
animation: none;
}
}
&--notAllowed:not(.tagify__tag--editable) {
div {
> span {
opacity: 0.5;
}
&::before {
box-shadow: 0 0 0 $tag-inset-shadow-size $tag-invalid-bg inset !important;
box-shadow: 0 0 0 var(--tag-inset-shadow-size, $tag-inset-shadow-size)
var(--tag-invalid-bg, $tag-invalid-bg) inset !important;
transition: 0.2s;
}
}
}
}
&[readonly] {
#{$self}__tag__removeBtn {
display: none;
}
> div {
// padding: $tag-pad;
&::before {
@include tagReadonlyBG;
}
}
}
&--editable {
> div {
color: $tag-text-color--edit;
color: var(--tag-text-color--edit, $tag-text-color--edit);
&::before {
box-shadow: 0 0 0 2px $tag-hover inset !important;
box-shadow: 0 0 0 2px var(--tag-hover, $tag-hover) inset !important;
}
}
> #{$self}__tag__removeBtn {
pointer-events: none;
&::after {
opacity: 0;
transform: translateX(100%) translateX(5px);
}
}
&.tagify--invalid {
> div {
&::before {
box-shadow: 0 0 0 2px $tag-invalid-color inset !important;
box-shadow: 0 0 0 2px var(--tag-invalid-color, $tag-invalid-color) inset !important;
}
}
}
}
&__removeBtn {
$size: 14px;
order: 5;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50px;
cursor: pointer;
font: #{$size} Serif;
background: $tag-remove-btn-bg;
background: var(--tag-remove-btn-bg, $tag-remove-btn-bg);
color: $tag-remove-btn-color;
color: var(--tag-remove-btn-color, $tag-remove-btn-color);
width: $size;
height: $size;
margin-right: $size / 3;
margin-left: auto;
overflow: hidden;
transition: 0.2s ease-out;
&::after {
content: '\00D7';
}
&:hover {
color: white;
background: $tag-remove-btn-bg--hover;
background: var(--tag-remove-btn-bg--hover, $tag-remove-btn-bg--hover);
+ div {
> span {
opacity: 0.5;
}
&::before {
box-shadow: 0 0 0 $tag-inset-shadow-size rgba($tag-remove, 0.3) inset !important;
box-shadow: 0 0 0 var(--tag-inset-shadow-size, $tag-inset-shadow-size)
var(--tag-remove-bg, rgba($tag-remove, 0.3)) inset !important;
transition: 0.2s;
}
}
}
}
}
&:not(#{$self}--mix) {
#{$self}__input {
// https://stackoverflow.com/a/13470210/104380
br {
display: none;
}
* {
display: inline;
white-space: nowrap;
}
}
}
///////////////////////////////////////////
// Holds the placeholder & the tags input
&__input {
$placeholder-width: 110px;
flex-grow: 1;
display: inline-block;
min-width: $placeholder-width;
margin: $tagMargin;
padding: $tag-pad;
padding: var(--tag-pad, $tag-pad);
line-height: inherit;
position: relative;
// #160 Line break (\n) as delimiter
white-space: pre-wrap;
color: $input-color;
color: var(--input-color, $input-color);
box-sizing: inherit;
&:empty {
@include firefox {
// clicking twice on the input (not fast) disallows typing (bug) only when the input has "display:flex".
// https://bugzilla.mozilla.org/show_bug.cgi?id=904846#c45
display: flex;
}
&::before {
@include placeholder;
display: inline-block;
width: auto;
#{ $self }--mix & {
display: inline-block;
}
}
}
&:focus {
outline: none;
&::before {
@include placeholder(false);
/* ALL MS BROWSERS: hide placeholder (on focus) otherwise the caret is places after it, which is weird */
/* IE10+ CSS styles go here */
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
display: none;
}
/* IE Edge 12+ CSS styles go here */
@supports (-ms-ime-align: auto) {
display: none;
}
}
&:empty {
&::before {
@include placeholder(true);
@include firefox {
// remove ":after" pseudo element: https://bugzilla.mozilla.org/show_bug.cgi?id=904846#c45
content: unset;
display: inline-block;
}
color: $placeholder-color-focus;
color: var(--placeholder-color-focus);
}
&::after {
@include firefox {
display: none;
}
}
}
}
&::before {
content: attr(data-placeholder);
height: 1em;
line-height: 1em;
margin: auto 0;
z-index: 1;
color: $placeholder-color;
color: var(--placeholder-color);
white-space: nowrap;
pointer-events: none;
opacity: 0;
position: absolute;
#{$self}--mix & {
display: none;
position: static;
line-height: inherit;
}
}
@supports (-moz-appearance: none) {
&::before {
line-height: inherit;
position: relative;
}
}
// tries to suggest the rest of the value from the first item in the whitelist which matches it
&::after {
content: attr(data-suggest);
display: inline-block;
/* allows spaces at the beginning */
white-space: pre;
color: $tag-text-color;
opacity: 0.3;
pointer-events: none;
max-width: 100px;
}
/*
in "mix mode" the tags are inside the "input" element
*/
#{$self}__tag {
// a developer can choose to have automatic horizontal margin ("1ch" advised) between tags or use manual keyboard spaces
// line-height: 1.1;
margin: 0;
> div {
padding-top: 0;
padding-bottom: 0;
}
}
}
&--mix {
// display:flex makes Chrome generates <div><br></div> when pressing ENTER key
display: block;
#{$self}__input {
padding: $tagMargin;
margin: 0;
width: 100%;
height: 100%;
line-height: 1.5;
// needed to resolve this bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1182621
display: block;
&::before {
height: auto;
}
// no suggested-complete are shown in mix-mode while higilighting dropdown options
&::after {
content: none;
}
}
}
&--select {
&::after {
$size: 16px;
content: '>';
opacity: 0.5;
position: absolute;
top: 50%;
right: 0;
bottom: 0;
font: $size monospace;
line-height: $size / 2;
height: $size / 2;
pointer-events: none;
transform: translate(-150%, -50%) scaleX(1.2) rotate(90deg);
transition: 0.2s ease-in-out;
}
&[aria-expanded='true'] {
&::after {
transform: translate(-150%, -50%) rotate(270deg) scaleY(1.2);
}
}
#{$self}__tag {
position: absolute;
top: 0;
right: 1.8em;
bottom: 0;
div {
display: none;
}
}
#{$self}__input {
width: 100%;
}
}
&--invalid {
--tags-border-color: #{$tag-invalid-color};
}
// Since the dropdown is an external element, which is positioned directly on the body element
// it cannot ingerit the CSS variables applied on the ".Tagify" element
&__dropdown {
$dropdown: &;
$trans: 0.25s cubic-bezier(0, 1, 0.5, 1);
position: absolute;
z-index: 9999;
transform: translateY(1px);
overflow: hidden;
&[placement='top'] {
margin-top: 0;
transform: translateY(-100%);
#{$dropdown}__wrapper {
border-top-width: 1px;
border-bottom-width: 0;
}
}
// when the dropdown shows next to the caret while typing
&[position='text'] {
box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), 0.1);
font-size: 0.9em;
#{$dropdown}__wrapper {
border-width: 1px;
}
}
&__wrapper {
@extend %box-radius-bottom;
max-height: 300px;
overflow: hidden;
background: $c-bg-popup;
border: 1px solid $tags-focus-border-color;
// fixes - https://bugs.chromium.org/p/chromium/issues/detail?id=1147523
border-width: 1.1px;
border-top-width: 0;
box-shadow: 0 2px 4px -2px rgba(black, 0.2);
transition: $trans;
&:hover {
overflow: auto;
}
}
// initial state, pre-rendered
&--initial {
#{$dropdown}__wrapper {
max-height: 20px;
transform: translateY(-1em);
}
&[placement='top'] {
#{$dropdown}__wrapper {
transform: translateY(2em);
}
}
}
&__item {
box-sizing: inherit;
padding: $tag-pad;
margin: 1px;
cursor: pointer;
border-radius: 2px;
position: relative;
outline: none;
&--active {
background: $tags-focus-border-color;
color: white;
}
&:active {
filter: brightness(105%);
}
}
}
}