Skip to content

Commit

Permalink
input-tag
Browse files Browse the repository at this point in the history
  • Loading branch information
tborychowski committed Sep 24, 2023
1 parent 2f16a22 commit ab2e84e
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 245 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
Changelog
=========

## v9.1.0 *(2023-09-22)*
## v9.1.0 *(2023-09-?)*
- New components `InputRating`, `Tag`.
- Add `hideTip` property to `Popover`.
- Small bugfixes and improvements.


Expand Down
16 changes: 15 additions & 1 deletion docs-src/components/popover/Popover.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@
</Popover>


<h3>No tip</h3>
<p>Styling is different than the normal popover, because the use-case for no-tip popover
is more similar to a dropdown rather than a tooltip or a popover,
so it makes sense that it also looks for the role.</p>

<Button on:click="{popover5.open}">Open popover</Button>
<Popover hideTip bind:this="{popover5}">
<h2>Context information</h2>
<p>Some text</p>
<Button on:click="{popover5.close}">Click me</Button>
</Popover>


<h3>Custom offset</h3>
<Button on:click="{popover2.open}">Open popover</Button>
<Popover bind:this="{popover2}" offset="-20">Smaller offset</Popover>
Expand Down Expand Up @@ -48,7 +61,7 @@ import { Popover, Button } from '../../../src';
import { API } from '../../api-table';
import { CodeExample } from '../../code-example';
let popover1, popover2, popover3, popover4;
let popover1, popover2, popover3, popover4, popover5;
let content = '<h2>Context information</h2><p>Some text</p>';
function updateContent () {
Expand All @@ -58,6 +71,7 @@ function updateContent () {
const apiProps = [
{ name: 'class', type: 'string', description: 'Additional css class name to be added to the component.' },
{ name: 'offset', type: 'number', default: '2', description: 'Customize popover offset. Use negative number for smaller offset or positive for bigger' },
{ name: 'hideTip', description: 'Display just the container, without the tip (small triangle pointing at the target).' },
{ name: 'position', type: ['top', 'bottom'], default: 'bottom', description: 'Prefer the position of the popover to be above (top) or below (bottom) the target element.' },
{ name: 'bind:element', type: 'element', description: 'Exposes the HTML element of the component.' },
{ name: 'bind:contentElement', type: 'element', description: 'Exposes the HTML element of the content div.' },
Expand Down
3 changes: 2 additions & 1 deletion docs-src/pages/changelog.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<h1>Changelog</h1>
<h2>v9.1.0 <em>(2023-09-22)</em></h2>
<h2>v9.1.0 <em>(2023-09-?)</em></h2>
<ul>
<li>New components <code>InputRating</code>, <code>Tag</code>.</li>
<li>Add <code>hideTip</code> property to <code>Popover</code>.</li>
<li>Small bugfixes and improvements.</li>
</ul>
<h2>v9.0.5 <em>(2023-09-22)</em></h2>
Expand Down
216 changes: 109 additions & 107 deletions docs/docs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/ui.css

Large diffs are not rendered by default.

46 changes: 12 additions & 34 deletions src/input/input-tag/InputTag.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
.input-tag .input-inner:focus { outline: none; }

.input-tag .input-inner[aria-expanded="true"] {
border-color: var(--ui-color-accent);
box-shadow: var(--ui-shadow-focus);
}


.input-tag .input-row {
min-height: calc(var(--ui-button-height) - 2px);
padding: 0.3rem 0.3rem calc(0.3rem - 1px);
Expand All @@ -7,31 +15,10 @@
flex-wrap: wrap;
}

.input-tag .input-row > .button { margin-left: auto; }
.input-tag .input-row > .icon { margin-top: 1px; }

.input-tag .input-row > .button,
.input-tag .input-row > .icon { position: static; flex-shrink: 0; }


.input-tag-list {
position: absolute;
z-index: var(--ui-z-index-popup);
overflow-y: auto;
overscroll-behavior-y: contain;
padding: 1rem;
min-height: 0;
max-height: 26rem;
-webkit-user-select: none;
user-select: none;
transform: translateZ(1px);
width: min-content;
color: var(--ui-color-text);
border: var(--ui-popup-border);
border-radius: calc(var(--ui-border-radius) + 0.2rem);
background: var(--ui-popup-background);
box-shadow: var(--ui-shadow-fancy);
}
.input-tag .input-row > .icon { position: static; flex-shrink: 0; margin-top: 1px; }


.input-tag-popover { width: 25ch; }

.input-tag-list-add-row {
display: flex;
Expand All @@ -49,12 +36,3 @@
gap: 0.5rem;
width: 100%;
}


@supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) {
.input-tag-list {
background-color: var(--ui-color-background-semi);
-webkit-backdrop-filter: blur(30px);
backdrop-filter: blur(30px);
}
}
154 changes: 69 additions & 85 deletions src/input/input-tag/InputTag.svelte
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
<!-- svelte-ignore a11y-no-static-element-interactions a11y-no-noninteractive-tabindex -->
<div
{title}
class="input input-tag {className}"
class:has-error="{error}"
class:has-value="{value !== ''}"
class:label-on-the-left="{labelOnTheLeft === true || labelOnTheLeft === 'true'}"
{title}
bind:this="{element}">

<Label {label} {disabled} for="{_id}"/>
<Info msg="{info}" />

<div class="input-inner" class:disabled>
<div
class="input-inner"
class:disabled
inert="{disabled}"
tabindex="0"
on:keydown="{onkeydown}"
on:click="{open}"
bind:this="{boxElement}">

<InputError id="{errorMessageId}" msg="{error}" />

<div class="input-row">
<Icon name="tag"/>
{#each _value as tag}
<Tag icon="close">{tag}</Tag>
<Tag noTabIndex icon="close"
on:click="{() => removeTagFromValue(tag)}">{tag}</Tag>
{/each}
<Button link
icon="add"
class="input-add-button"
{disabled}
on:click="{toggle}"/>

<input
{name}
Expand All @@ -34,29 +39,32 @@
</div>
</div>

{#if opened}
<div
id="input-tag-list-{listId}"
class="input-tag-list {opened ? '' : 'hidden'}"
role="listbox"
bind:this="{listElement}">
<div class="input-tag-list-tags">
{#each tags as tag}
<Tag icon="add">{tag.text}</Tag>
{/each}
</div>
<form class="input-tag-list-add-row" on:submit|preventDefault="{addTag}">
<InputText bind:value="{newTagName}"/>
<Button submit link icon="add"/>
</form>
<Popover
hideTip
dontHideOnTargetClick
class="input-tag-popover"
on:close="{onclose}"
bind:element="{listElement}"
bind:this="{listPopover}">

<div class="input-tag-list-tags">
{#each tags as tag}
<Tag icon="add"
on:click="{() => addTagToValue(tag.text)}">{tag.text}</Tag>
{/each}
</div>
{/if}
<form class="input-tag-list-add-row" on:submit|preventDefault="{addTag}">
<InputText bind:value="{newTagName}"/>
<Button submit link icon="add"/>
</form>
</Popover>

<script>
import { InputText } from '../input-text';
import { Button } from '../../button';
import { Popover } from '../../popover';
import { Tag } from '../../tag';
import { guid, alignItem, FOCUSABLE_SELECTOR } from '../../utils';
import { guid } from '../../utils';
import { Icon } from '../../icon';
import { Info } from '../../info-bar';
import { InputError } from '../input-error';
Expand All @@ -77,46 +85,61 @@ export let value = '';
export let tags = [];
export let element = undefined;
export let boxElement = undefined;
export let inputElement = undefined;
export let listElement = undefined;
const listId = guid();
const errorMessageId = guid();
let opened = false;
let newTagName = '';
let opened = false;
let listPopover;
$:_id = id || name || guid();
$:_value = value.split(/[, ;]/).map(tag => tag.trim()).filter(tag => tag !== '');
$:_value = valueToArray(value);
function toggle () {
if (opened) close();
else open();
function valueToArray (val) {
return val.split(/[, ;]/).map(tag => tag.trim()).filter(tag => tag !== '');
}
function open (e) {
function open () {
if (opened) return;
opened = true;
requestAnimationFrame(() => {
if (listElement.parentElement !== document.body) {
document.body.appendChild(listElement);
}
addEventListeners();
alignDropdown(e);
listElement.querySelector(FOCUSABLE_SELECTOR).focus();
});
listPopover.open(boxElement);
}
function close () {
function updatePosition () {
if (!opened) return;
removeEventListeners();
requestAnimationFrame(listPopover.updatePosition);
}
function onclose () {
opened = false;
}
function onkeydown (e) {
if (e.key === 'Enter') open();
}
function addTagToValue (tag) {
const val = valueToArray(value);
val.push(tag);
// unique list of tags
value = [...new Set(val)].join(', ');
updatePosition();
}
function removeTagFromValue (tag) {
const val = valueToArray(value).filter(t => t !== tag);
// wait for docclick in popover to fire
// before removing the tag from the value
// so that the popover can check that the click was within the target and do nothing
requestAnimationFrame(() => {
value = val.join(', ');
updatePosition();
});
}
function addTag () {
const newTags = newTagName
Expand All @@ -129,43 +152,4 @@ function addTag () {
newTagName = '';
}
function alignDropdown () {
alignItem({
element: listElement,
target: element,
setMinWidthToTarget: true,
});
}
function onResize () {
if (!opened) return;
return close();
}
function onViewportResize () {
if (!opened) return;
alignDropdown();
}
function onDocumentClick (e) {
const notEl = element && !element.contains(e.target);
const notList = listElement && !listElement.contains(e.target);
if (open && notEl && notList) close();
}
function addEventListeners () {
window.addEventListener('resize', onResize);
document.addEventListener('click', onDocumentClick, true);
window.visualViewport.addEventListener('resize', onViewportResize);
}
function removeEventListeners () {
window.removeEventListener('resize', onResize);
document.removeEventListener('click', onDocumentClick, true);
window.visualViewport.removeEventListener('resize', onViewportResize);
}
</script>
Loading

0 comments on commit ab2e84e

Please sign in to comment.