UBERF-5394: Create component for new search input (#4777)

* UBERF-5394: Create component for new search input

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* chore(SearchPicker): cleanup

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* fix(SearchPickerItem): truncate text

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* chore(SearchPicker): flex width

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* fix: better styles & light theme

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* chore(SearchPicker): move borders and outlines to `rem`

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* chore: cleanup

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* feat: better styles, removable badges via `Backspace`

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* chore: cleanup

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* refactor(Chip): use `ButtonIcon`

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

* chore: format

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>

---------

Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>
This commit is contained in:
Eduard Aksamitov 2024-02-29 11:56:24 +03:00 committed by GitHub
parent 58d39f0cd6
commit 7224bc625a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 228 additions and 4 deletions

View File

@ -7,7 +7,9 @@
* {
--global-accent-IconColor: #6796FF;
--global-on-accent-TextColor: #FFFFFF;
--global-focus-inset-BorderColor: #0D121C;
--global-ui-hover-OverlayColor: #15307226;
--global-ui-active-OverlayColor: #15307233;
--button-accent-LabelColor: #fff;
--button-disabled-LabelColor: #8b97ad;
@ -22,6 +24,8 @@
--button-negative-BorderColor: #d1d5de26;
--button-negative-hover-BackgroundColor: #e34748;
--button-negative-active-BackgroundColor: #c42a32;
--tag-on-accent-PorpoiseText: #FFFFFF;
}
/* Dark Theme */
@ -29,6 +33,7 @@
--global-ui-BackgroundColor: #A5BDFF0D;
--global-ui-BorderColor: #A5BDFF1A;
--global-ui-hover-BackgroundColor: #A5BDFF1A;
--global-ui-active-BackgroundColor: #A5BDFF26;
--global-ui-highlight-BackgroundColor: #A5BDFF0D;
--global-ui-hover-highlight-BackgroundColor: #A5BDFF26;
--global-surface-01-BackgroundColor: #131925;
@ -49,10 +54,12 @@
--global-accent-TextColor: #4D7FF5;
--global-error-TextColor: #FF6359;
--global-focus-BorderColor: #2A59D6;
--global-focus-inset-BorderColor: #0D121C;
--global-popover-ShadowColor: #0E131E59;
--global-modal-ShadowColor: #0E131E73;
--global-higlight-Color: #F76E53;
--global-accent-SkyText: #B9D1F5;
--global-accent-BackgroundColor: #204DC8;
--tag-on-subtle-PorpoiseText: #F2F4F6;
--tag-subtle-PorpoiseBackground: #343F49;
@ -91,6 +98,7 @@
--global-ui-BackgroundColor: #1530720D;
--global-ui-BorderColor: #1530721A;
--global-ui-hover-BackgroundColor: #1530721A;
--global-ui-active-BackgroundColor: #A5BDFF40;
--global-ui-highlight-BackgroundColor: #A5BDFF26;
--global-ui-hover-highlight-BackgroundColor: #A5BDFF40;
--global-surface-01-BackgroundColor: #F8F9FA;
@ -111,10 +119,12 @@
--global-accent-TextColor: #3566E2;
--global-error-TextColor: #A40A1B;
--global-focus-BorderColor: #204DC8;
--global-focus-inset-BorderColor: #FFFFFF;
--global-popover-ShadowColor: #0E131E1F;
--global-modal-ShadowColor: #0E131E14;
--global-higlight-Color: #F76E53;
--global-accent-SkyText:#B9D1F5;
--global-accent-BackgroundColor: #3566E2;
--tag-on-subtle-PorpoiseText: #293139;
--tag-subtle-PorpoiseBackground: #C8D1D9;

View File

@ -29,7 +29,7 @@
export let iconSize: IconSize | undefined = undefined
export let iconProps: any | undefined = undefined
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative'
export let size: 'large' | 'medium' | 'small' | 'extra-small'
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min'
export let disabled: boolean = false
export let loading: boolean = false
export let pressed: boolean = false
@ -48,6 +48,10 @@
actualIconSize = 'medium'
}
export function focus () {
element?.focus()
}
// Focusable control with index
export let focusIndex = -1
const { idx, focusManager } = registerFocus(focusIndex, {
@ -83,6 +87,7 @@
disabled={loading || disabled}
use:tp={tooltip}
on:click
on:keydown
>
{#if loading}
<div class="icon animate"><Spinner size={type === 'type-button' && !hasMenu ? 'medium' : 'small'} /></div>
@ -172,6 +177,11 @@
width: var(--global-extra-small-Size);
}
}
&.min {
height: var(--global-min-Size);
border: 0;
border-radius: var(--min-BorderRadius);
}
&.type-button-icon .icon,
&.menu .icon {
width: var(--spacing-2);

View File

@ -19,7 +19,7 @@
import ButtonBase from './ButtonBase.svelte'
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary'
export let size: 'large' | 'medium' | 'small' | 'extra-small' = 'large'
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
export let icon: Asset | AnySvelteComponent | ComponentType
export let iconProps: any | undefined = undefined
export let disabled: boolean = false
@ -29,9 +29,16 @@
export let inheritColor: boolean = false
export let tooltip: LabelAndProps | undefined = undefined
export let focusIndex = -1
let element: ButtonBase | undefined
export function focus () {
element?.focus()
}
</script>
<ButtonBase
bind:this={element}
type={'type-button-icon'}
{kind}
{size}
@ -45,4 +52,5 @@
{tooltip}
{focusIndex}
on:click
on:keydown
/>

View File

@ -0,0 +1,80 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { IconClose, ButtonIcon } from '..'
export let label: string
const dispatch = createEventDispatcher()
let buttonRef: ButtonIcon | undefined
function handleBackspace (event: KeyboardEvent) {
if (event.key === 'Backspace') {
dispatch('remove')
}
}
export function focus () {
buttonRef?.focus()
}
</script>
<div class="flex items-center font-medium-14 max-w-60 p-1 chip">
<span class="px-2 overflow-label">{label}</span>
<ButtonIcon
bind:this={buttonRef}
kind="tertiary"
size="min"
icon={IconClose}
inheritColor={true}
on:click={() => dispatch('remove')}
on:keydown={handleBackspace}
/>
</div>
<style lang="scss">
.chip {
position: relative;
height: var(--global-small-Size);
border: none;
color: var(--tag-on-accent-PorpoiseText);
background-color: var(--global-accent-BackgroundColor);
border-radius: var(--small-BorderRadius);
&:after {
content: '';
position: absolute;
inset: 0;
border-radius: var(--small-BorderRadius);
overflow: hidden;
background-color: transparent;
pointer-events: none;
}
&:hover:after {
background-color: var(--global-ui-hover-OverlayColor);
}
&:active:after {
background-color: var(--global-ui-active-OverlayColor);
}
& :global(button.type-button-icon) {
margin-right: var(--spacing-0_25);
}
}
</style>

View File

@ -0,0 +1,114 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import Chip from './Chip.svelte'
interface Item {
id: string
label: string
}
export let autoFocus: boolean = false
export let placeholder = ''
export let value = ''
export let items: Item[] = []
const dispatch = createEventDispatcher()
let inputRef: HTMLInputElement | undefined
const itemsRef: Chip[] = []
function handleBackspace (event: KeyboardEvent) {
if (event.key === 'Backspace' && value === '' && items.length > 0) {
itemsRef[items.length - 1].focus()
}
dispatch('keydown', event)
}
function handleItemRemove (id: string) {
dispatch('item-remove', id)
inputRef?.focus()
}
export function focus () {
inputRef?.focus()
autoFocus = false
}
$: if (inputRef !== undefined && autoFocus) {
focus()
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="flex flex-gap-1 flex-wrap search-picker" on:click={() => inputRef?.focus()}>
{#each items as item, i}
<Chip
bind:this={itemsRef[i]}
label={item.label}
on:remove={() => {
handleItemRemove(item.id)
}}
/>
{/each}
<input
bind:this={inputRef}
class="flex-grow font-regular-14"
type="text"
autocomplete="off"
spellcheck="false"
{placeholder}
bind:value
on:change
on:input
on:keydown={handleBackspace}
/>
</div>
<style lang="scss">
.search-picker {
position: relative;
width: 100%;
padding: var(--spacing-0_5);
border-radius: var(--small-focus-BorderRadius);
box-shadow: inset 0 0 0 0.0625rem var(--input-BorderColor);
cursor: text;
&:focus-within {
box-shadow:
inset 0 0 0 0.0625rem var(--input-BorderColor),
0 0 0 0.125rem var(--global-focus-inset-BorderColor);
outline: 0.125rem solid var(--global-focus-BorderColor);
outline-offset: 0.125rem;
}
input {
height: var(--global-small-Size);
padding: 0;
border: none;
caret-color: var(--input-search-IconColor);
&::placeholder {
color: var(--global-disabled-TextColor);
}
&:only-child {
padding: var(--spacing-0_5) var(--spacing-1_25);
}
}
}
</style>

View File

@ -97,6 +97,8 @@ export { default as Row } from './components/Row.svelte'
// export { default as CheckBoxList } from './components/CheckBoxList.svelte.txt'
export { default as EditWithIcon } from './components/EditWithIcon.svelte'
export { default as SearchEdit } from './components/SearchEdit.svelte'
export { default as SearchPicker } from './components/SearchPicker.svelte'
export { default as Chip } from './components/Chip.svelte'
export { default as Loading } from './components/Loading.svelte'
export { default as Spinner } from './components/Spinner.svelte'
export { default as Popup } from './components/Popup.svelte'

View File

@ -39,7 +39,7 @@
let listSelection = 0
let list: ListView
let selectedItems = new Set<Ref<Employee>>(selected)
$: selectedItems = new Set<Ref<Employee>>(selected)
let persons: Employee[] = []