mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' of https://github.com/appwrite/appwrite-console-poc into refactor-layout-state
This commit is contained in:
Generated
+1
@@ -11,6 +11,7 @@
|
||||
"@aw-labs/appwrite-console": "^6.0.0",
|
||||
"@aw-labs/icons": "0.0.0-64",
|
||||
"@aw-labs/ui": "0.0.0-64",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"echarts": "^5.4.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@
|
||||
"@aw-labs/appwrite-console": "^6.0.0",
|
||||
"@aw-labs/icons": "0.0.0-64",
|
||||
"@aw-labs/ui": "0.0.0-64",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"echarts": "^5.4.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
@@ -35,8 +36,8 @@
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/svelte": "3.1.3",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
||||
"@typescript-eslint/parser": "^5.40.0",
|
||||
"@vitest/ui": "^0.24.3",
|
||||
|
||||
@@ -1,21 +1,81 @@
|
||||
<script lang="ts">
|
||||
import { slide } from 'svelte/transition';
|
||||
import { createPopper, type Instance } from '@popperjs/core';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
||||
export let show = false;
|
||||
export let position: 'top' | 'bottom' = 'top';
|
||||
export let horizontal: 'left' | 'right' = 'right';
|
||||
export let arrowPosition: 'start' | 'center' | 'end' = 'start';
|
||||
export let arrow = true;
|
||||
export let noArrow = false;
|
||||
export let placement: Placement = 'bottom-start';
|
||||
export let scrollable = false;
|
||||
export let childStart = false;
|
||||
|
||||
let parentElement: HTMLDivElement;
|
||||
type Placement = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
|
||||
let element: HTMLDivElement;
|
||||
let tooltip: HTMLDivElement;
|
||||
let instance: Instance;
|
||||
let currentArrow: Placement;
|
||||
|
||||
$: [arrowHorizontal, arrowPosition] = applyArrow(currentArrow);
|
||||
|
||||
function applyArrow(value: Placement): [string: 'start' | 'end', string: 'top' | 'bottom'] {
|
||||
switch (value) {
|
||||
case 'bottom-start':
|
||||
return ['start', 'bottom'];
|
||||
case 'bottom-end':
|
||||
return ['end', 'bottom'];
|
||||
case 'top-start':
|
||||
return ['start', 'top'];
|
||||
case 'top-end':
|
||||
return ['end', 'top'];
|
||||
default:
|
||||
return ['start', 'bottom'];
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
instance = createPopper(element, tooltip, {
|
||||
placement,
|
||||
onFirstUpdate(state) {
|
||||
if (currentArrow !== state.placement) {
|
||||
currentArrow = state.placement as Placement;
|
||||
}
|
||||
},
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, noArrow ? 0 : 12]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'flip',
|
||||
options: {
|
||||
fallbackPlacements: ['top-start', 'top-end', 'bottom-start', 'bottom-end']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'placementLogger',
|
||||
enabled: true,
|
||||
phase: 'main',
|
||||
fn({ state }) {
|
||||
if (currentArrow !== state.placement) {
|
||||
currentArrow = state.placement as Placement;
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
$: if (show) {
|
||||
instance?.update();
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
instance?.destroy();
|
||||
});
|
||||
|
||||
const onBlur = (event: MouseEvent) => {
|
||||
if (
|
||||
show &&
|
||||
!(event.target === parentElement || parentElement.contains(event.target as Node))
|
||||
) {
|
||||
if (show && !(event.target === element || element.contains(event.target as Node))) {
|
||||
show = false;
|
||||
}
|
||||
};
|
||||
@@ -23,21 +83,20 @@
|
||||
|
||||
<svelte:window on:click={onBlur} />
|
||||
|
||||
<div
|
||||
class="drop-wrapper"
|
||||
class:is-open={show}
|
||||
class:u-cross-child-start={childStart}
|
||||
bind:this={parentElement}>
|
||||
<div class="drop-wrapper" class:u-cross-child-start={childStart} bind:this={element}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div bind:this={tooltip} style="z-index: 10">
|
||||
{#if show}
|
||||
<div
|
||||
class="drop"
|
||||
class:is-arrow-center={arrowPosition === 'center'}
|
||||
class:is-arrow-end={arrowPosition === 'end'}
|
||||
class:is-no-arrow={!arrow}
|
||||
class:is-block-end={position === 'bottom'}
|
||||
class:is-inline-end={horizontal === 'left'}
|
||||
transition:slide={{ duration: 100 }}>
|
||||
style="position: revert"
|
||||
class:is-no-arrow={noArrow}
|
||||
class:is-arrow-start={arrowHorizontal === 'start'}
|
||||
class:is-arrow-end={arrowHorizontal === 'end'}
|
||||
class:is-block-start={arrowPosition === 'top'}
|
||||
class:is-block-end={arrowPosition === 'bottom'}>
|
||||
<section
|
||||
class:u-overflow-y-auto={scrollable}
|
||||
class:u-max-height-200={scrollable}
|
||||
|
||||
@@ -21,12 +21,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<DropList
|
||||
bind:show={showDropdown}
|
||||
position="bottom"
|
||||
horizontal="right"
|
||||
arrow={true}
|
||||
arrowPosition="start">
|
||||
<DropList bind:show={showDropdown} placement="bottom-end">
|
||||
<Button text noMargin on:click={() => (showDropdown = !showDropdown)}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add role</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { onMount } from 'svelte';
|
||||
import { AvatarInitials, DropList, DropListItem, DropListLink } from '$lib/components';
|
||||
import { AvatarInitials, DropListItem, DropListLink } from '$lib/components';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { organizationList, organization, newOrgModal } from '$lib/stores/organization';
|
||||
@@ -10,8 +10,10 @@
|
||||
import LightMode from '$lib/images/mode/light-mode.svg';
|
||||
import DarkMode from '$lib/images/mode/dark-mode.svg';
|
||||
import SystemMode from '$lib/images/mode/system-mode.svg';
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
let showDropdown = false;
|
||||
let droplistElement: HTMLDivElement;
|
||||
|
||||
onMount(async () => {
|
||||
await organizationList.load();
|
||||
@@ -19,8 +21,19 @@
|
||||
await organization.load($organizationList.teams[0].$id);
|
||||
}
|
||||
});
|
||||
|
||||
const onBlur = (event: MouseEvent) => {
|
||||
if (
|
||||
showDropdown &&
|
||||
!(event.target === droplistElement || droplistElement.contains(event.target as Node))
|
||||
) {
|
||||
showDropdown = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:window on:click={onBlur} />
|
||||
|
||||
<a
|
||||
class="logo"
|
||||
href={$organization ? `${base}/console/organization-${$organization.$id}` : `${base}/console`}>
|
||||
@@ -44,12 +57,7 @@
|
||||
</nav>
|
||||
<nav class="user-profile">
|
||||
{#if $user}
|
||||
<DropList
|
||||
bind:show={showDropdown}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}
|
||||
scrollable={true}>
|
||||
<div class="drop-wrapper" class:is-open={showDropdown} bind:this={droplistElement}>
|
||||
<button class="user-profile-button" on:click={() => (showDropdown = !showDropdown)}>
|
||||
<AvatarInitials size={40} name={$user.name} />
|
||||
<span class="user-profile-info is-only-desktop">
|
||||
@@ -64,70 +72,80 @@
|
||||
class:icon-cheveron-up={showDropdown}
|
||||
class:icon-cheveron-down={!showDropdown} />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
{#if $organizationList?.total}
|
||||
{#each $organizationList.teams as org}
|
||||
<DropListLink
|
||||
href={`${base}/console/organization-${org.$id}`}
|
||||
on:click={() => {
|
||||
showDropdown = false;
|
||||
}}>{org.name}</DropListLink>
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="other">
|
||||
<section class="drop-section">
|
||||
<ul class="drop-list">
|
||||
<DropListItem
|
||||
icon="plus"
|
||||
on:click={() => {
|
||||
showDropdown = false;
|
||||
newOrgModal.set(true);
|
||||
}}>New organization</DropListItem>
|
||||
<DropListLink
|
||||
href={`${base}/console/account`}
|
||||
on:click={() => (showDropdown = false)}>Your Account</DropListLink>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="drop-section">
|
||||
<ul class="u-flex u-gap-12">
|
||||
<li>
|
||||
<label class="image-radio">
|
||||
<img src={LightMode} alt="light mode" />
|
||||
<input
|
||||
type="radio"
|
||||
class="is-small"
|
||||
name="mode"
|
||||
bind:group={$app.theme}
|
||||
value="light" />
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="image-radio">
|
||||
<img src={DarkMode} alt="dark mode" />
|
||||
<input
|
||||
type="radio"
|
||||
class="is-small"
|
||||
name="mode"
|
||||
bind:group={$app.theme}
|
||||
value="dark" />
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="image-radio">
|
||||
<img src={SystemMode} alt="system mode" />
|
||||
<input
|
||||
type="radio"
|
||||
class="is-small"
|
||||
name="mode"
|
||||
bind:group={$app.theme}
|
||||
value="auto" />
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{#if showDropdown}
|
||||
<div
|
||||
class="drop is-no-arrow is-block-end is-inline-end"
|
||||
transition:slide={{ duration: 100 }}>
|
||||
<section class="drop-section u-overflow-y-auto u-max-height-200">
|
||||
<ul class="drop-list">
|
||||
{#if $organizationList?.total}
|
||||
{#each $organizationList.teams as org}
|
||||
<DropListLink
|
||||
href={`${base}/console/organization-${org.$id}`}
|
||||
on:click={() => {
|
||||
showDropdown = false;
|
||||
}}>{org.name}</DropListLink>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
</section>
|
||||
<section class="drop-section">
|
||||
<ul class="drop-list">
|
||||
<DropListItem
|
||||
icon="plus"
|
||||
on:click={() => {
|
||||
showDropdown = false;
|
||||
newOrgModal.set(true);
|
||||
}}>
|
||||
New organization
|
||||
</DropListItem>
|
||||
<DropListLink
|
||||
href={`${base}/console/account`}
|
||||
on:click={() => (showDropdown = false)}>
|
||||
Your Account
|
||||
</DropListLink>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="drop-section">
|
||||
<ul class="u-flex u-gap-12">
|
||||
<li>
|
||||
<label class="image-radio">
|
||||
<img src={LightMode} alt="light mode" />
|
||||
<input
|
||||
type="radio"
|
||||
class="is-small"
|
||||
name="mode"
|
||||
bind:group={$app.theme}
|
||||
value="light" />
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="image-radio">
|
||||
<img src={DarkMode} alt="dark mode" />
|
||||
<input
|
||||
type="radio"
|
||||
class="is-small"
|
||||
name="mode"
|
||||
bind:group={$app.theme}
|
||||
value="dark" />
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="image-radio">
|
||||
<img src={SystemMode} alt="system mode" />
|
||||
<input
|
||||
type="radio"
|
||||
class="is-small"
|
||||
name="mode"
|
||||
bind:group={$app.theme}
|
||||
value="auto" />
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -249,12 +249,7 @@
|
||||
</Button>
|
||||
{#if $user.status}
|
||||
{#if $user.phone && $user.email}
|
||||
<DropList
|
||||
bind:show={showVerifcationDropdown}
|
||||
position="top"
|
||||
horizontal="left"
|
||||
arrow={true}
|
||||
arrowPosition="end">
|
||||
<DropList bind:show={showVerifcationDropdown} placement="top-start">
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => (showVerifcationDropdown = !showVerifcationDropdown)}>
|
||||
|
||||
+2
-3
@@ -75,9 +75,8 @@
|
||||
<TableCell showOverflow>
|
||||
<DropList
|
||||
bind:show={showDropdown[index]}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}>
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="More options"
|
||||
|
||||
+1
-5
@@ -108,11 +108,7 @@
|
||||
{index.orders}
|
||||
</TableCellText>
|
||||
<TableCell showOverflow>
|
||||
<DropList
|
||||
bind:show={showDropdown[i]}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}>
|
||||
<DropList bind:show={showDropdown[i]} placement="bottom-start" noArrow>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="More options"
|
||||
|
||||
@@ -192,9 +192,8 @@
|
||||
<TableCell showOverflow>
|
||||
<DropList
|
||||
bind:show={showDropdown[index]}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}>
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="More options"
|
||||
|
||||
+2
-3
@@ -496,9 +496,8 @@
|
||||
<td class="table-col u-overflow-visible" data-title="options">
|
||||
<DropList
|
||||
bind:show={showVariablesDropdown[i]}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}>
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<button
|
||||
class="button is-text is-only-icon"
|
||||
aria-label="more options"
|
||||
|
||||
@@ -57,11 +57,7 @@
|
||||
<Secret value={variable.value} />
|
||||
</TableCell>
|
||||
<TableCell showOverflow title="options">
|
||||
<DropList
|
||||
bind:show={showDropdown[i]}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}>
|
||||
<DropList bind:show={showDropdown[i]} placement="bottom-start" noArrow>
|
||||
<button
|
||||
class="button is-text is-only-icon"
|
||||
aria-label="more options"
|
||||
|
||||
@@ -104,9 +104,7 @@
|
||||
</div>
|
||||
<DropList
|
||||
bind:show={showPeriodBandwidth}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrowPosition="end"
|
||||
placement="bottom-start"
|
||||
childStart>
|
||||
<button
|
||||
class="transparent-button"
|
||||
@@ -147,9 +145,7 @@
|
||||
</div>
|
||||
<DropList
|
||||
bind:show={showPeriodRequests}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrowPosition="end"
|
||||
placement="bottom-start"
|
||||
childStart>
|
||||
<button
|
||||
class="transparent-button"
|
||||
|
||||
@@ -37,12 +37,7 @@
|
||||
<div class="common-section u-flex u-gap-12">
|
||||
<Heading tag="h3" size="7">Platforms</Heading>
|
||||
<span class="u-margin-inline-start-auto">
|
||||
<DropList
|
||||
bind:show={showDropdown}
|
||||
position="bottom"
|
||||
horizontal="right"
|
||||
arrow={true}
|
||||
arrowPosition="start">
|
||||
<DropList bind:show={showDropdown} placement="bottom-start">
|
||||
<Button on:click={() => (showDropdown = !showDropdown)}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add platform</span>
|
||||
|
||||
@@ -154,9 +154,8 @@
|
||||
<TableCell showOverflow>
|
||||
<DropList
|
||||
bind:show={showDropdown[index]}
|
||||
position="bottom"
|
||||
horizontal="left"
|
||||
arrow={false}>
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="More options"
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import { tick } from 'svelte';
|
||||
import { render, fireEvent } from '@testing-library/svelte';
|
||||
import { DropList } from '../../../src/lib/components';
|
||||
|
||||
const data = {
|
||||
show: true,
|
||||
position: 'top',
|
||||
horizontal: 'right'
|
||||
show: true
|
||||
};
|
||||
|
||||
test('shows drop list component', () => {
|
||||
@@ -31,11 +30,11 @@ test('hide drop list on wrapper click', async () => {
|
||||
expect(component.show).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('drop list visible on list click', async () => {
|
||||
render(DropList, { ...data });
|
||||
const dropList = document.querySelector('ul.drop-list');
|
||||
test('hide drop list on list click', async () => {
|
||||
const { container } = render(DropList, { ...data });
|
||||
const dropList = container.querySelector('ul.drop-list');
|
||||
|
||||
await fireEvent.click(dropList);
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
expect(dropList).toBeInTheDocument();
|
||||
await tick();
|
||||
expect(dropList).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user