mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
add: editor support on mobile.
This commit is contained in:
+4
@@ -11,6 +11,7 @@
|
||||
let {
|
||||
show = $bindable(false),
|
||||
title,
|
||||
headerEnd,
|
||||
closeOnBlur = false,
|
||||
submit,
|
||||
cancel,
|
||||
@@ -51,6 +52,7 @@
|
||||
| undefined;
|
||||
children?: Snippet;
|
||||
footer?: Snippet | null;
|
||||
headerEnd?: Snippet | null;
|
||||
} & HTMLAttributes<HTMLDivElement> = $props();
|
||||
|
||||
let form: Form;
|
||||
@@ -90,6 +92,8 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{@render headerEnd?.()}
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
|
||||
|
||||
+19
-4
@@ -8,11 +8,24 @@
|
||||
let {
|
||||
children,
|
||||
noSqlEditor,
|
||||
sideSheetHeaderAction,
|
||||
sideSheetOptions = null,
|
||||
showEditorSideSheet = $bindable(false)
|
||||
}: {
|
||||
children: Snippet;
|
||||
noSqlEditor?: Snippet;
|
||||
sideSheetHeaderAction?: Snippet;
|
||||
showEditorSideSheet?: boolean;
|
||||
sideSheetOptions?: {
|
||||
sideSheetTitle?: string;
|
||||
submit?:
|
||||
| {
|
||||
text: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => boolean | void | Promise<boolean | void>;
|
||||
}
|
||||
| undefined;
|
||||
};
|
||||
} = $props();
|
||||
|
||||
let spreadsheetWrapper: HTMLDivElement;
|
||||
@@ -133,12 +146,14 @@
|
||||
{:else}
|
||||
<SideSheet
|
||||
noContentPadding
|
||||
title="Edit document"
|
||||
bind:show={showEditorSideSheet}
|
||||
submit={{
|
||||
text: 'Update'
|
||||
}}>
|
||||
submit={sideSheetOptions?.submit}
|
||||
title={sideSheetOptions?.sideSheetTitle ?? 'Edit document'}>
|
||||
{@render noSqlEditor?.()}
|
||||
|
||||
{#snippet headerEnd()}
|
||||
{@render sideSheetHeaderAction?.()}
|
||||
{/snippet}
|
||||
</SideSheet>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
+60
-56
@@ -73,12 +73,13 @@
|
||||
data?: JsonValue;
|
||||
isSaving?: boolean;
|
||||
loading?: boolean;
|
||||
onChange?: (newData: JsonValue) => Promise<void> | void;
|
||||
onChange?: (newData: JsonValue, hasChanged: boolean) => Promise<void> | void;
|
||||
onSave?: (newData: JsonValue) => Promise<void> | void;
|
||||
readonly?: boolean;
|
||||
wrapLines?: boolean;
|
||||
errorInPlace?: boolean;
|
||||
ctrlSave?: boolean;
|
||||
showHeaderActions?: boolean;
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -91,7 +92,8 @@
|
||||
readonly = false,
|
||||
wrapLines = true,
|
||||
errorInPlace = true,
|
||||
ctrlSave = false
|
||||
ctrlSave = false,
|
||||
showHeaderActions = true
|
||||
}: Props = $props();
|
||||
|
||||
let editorContainer: HTMLDivElement = $state(null);
|
||||
@@ -794,7 +796,7 @@
|
||||
}
|
||||
|
||||
data = parsed;
|
||||
onChange?.(parsed);
|
||||
onChange?.(parsed, hasDataChanged);
|
||||
lastExpectedContent = dataToString(parsed);
|
||||
}, DEBOUNCE_DELAY);
|
||||
}),
|
||||
@@ -897,65 +899,67 @@
|
||||
</script>
|
||||
|
||||
<div class="editor-container" class:loading>
|
||||
<div class="editor-header">
|
||||
{#if loading}
|
||||
<Skeleton variant="line" height="12px" width="143px" />
|
||||
{/if}
|
||||
{#if showHeaderActions}
|
||||
<div class="editor-header">
|
||||
{#if loading}
|
||||
<Skeleton variant="line" height="12px" width="143px" />
|
||||
{/if}
|
||||
|
||||
<Layout.Stack direction="row">
|
||||
{#if documentId && !loading}
|
||||
<div class="id-tag-button-wrapper">
|
||||
<Id value={documentId} tooltipPlacement="top">{truncateId(documentId)}</Id>
|
||||
<Layout.Stack direction="row">
|
||||
{#if documentId && !loading}
|
||||
<div class="id-tag-button-wrapper">
|
||||
<Id value={documentId} tooltipPlacement="top">{truncateId(documentId)}</Id>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{#if errorMessage && !$isSmallViewport && !loading}
|
||||
<div class="editor-header">
|
||||
<span class="error-message">{errorMessage}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{#if errorMessage && !$isSmallViewport && !loading}
|
||||
<div class="editor-header">
|
||||
<span class="error-message">{errorMessage}</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if documentId}
|
||||
<Layout.Stack direction="row" inline gap="s">
|
||||
<Tooltip placement="top" disabled={!hasDataChanged}>
|
||||
<Button
|
||||
icon
|
||||
secondary
|
||||
size="xs"
|
||||
disabled={!hasDataChanged}
|
||||
class="icon-button"
|
||||
on:click={handleSave}>
|
||||
{#if isSaving}
|
||||
<Spinner size="s" />
|
||||
{:else}
|
||||
<Icon icon={IconCheck} size="s" />
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
{#if documentId}
|
||||
<Layout.Stack direction="row" inline gap="s">
|
||||
<Tooltip placement="top" disabled={!hasDataChanged}>
|
||||
<Button
|
||||
icon
|
||||
secondary
|
||||
size="xs"
|
||||
disabled={!hasDataChanged}
|
||||
class="icon-button"
|
||||
on:click={handleSave}>
|
||||
{#if isSaving}
|
||||
<Spinner size="s" />
|
||||
{:else}
|
||||
<Icon icon={IconCheck} size="s" />
|
||||
{/if}
|
||||
</Button>
|
||||
<span slot="tooltip">Save</span>
|
||||
</Tooltip>
|
||||
|
||||
<span slot="tooltip">Save</span>
|
||||
</Tooltip>
|
||||
<Tooltip placement="top">
|
||||
<Button
|
||||
icon
|
||||
secondary
|
||||
size="xs"
|
||||
class="icon-button"
|
||||
disabled={loading}
|
||||
on:click={async () => {
|
||||
await copy(JSON.stringify(data, null, 2));
|
||||
tooltipMessage = 'Copied';
|
||||
setTimeout(() => (tooltipMessage = 'Copy document'), 1000);
|
||||
}}>
|
||||
<Icon icon={IconDuplicate} size="s" />
|
||||
</Button>
|
||||
|
||||
<Tooltip placement="top">
|
||||
<Button
|
||||
icon
|
||||
secondary
|
||||
size="xs"
|
||||
class="icon-button"
|
||||
disabled={loading}
|
||||
on:click={async () => {
|
||||
await copy(JSON.stringify(data, null, 2));
|
||||
tooltipMessage = 'Copied';
|
||||
setTimeout(() => (tooltipMessage = 'Copy document'), 1000);
|
||||
}}>
|
||||
<Icon icon={IconDuplicate} size="s" />
|
||||
</Button>
|
||||
|
||||
<span slot="tooltip">{tooltipMessage}</span>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</div>
|
||||
<span slot="tooltip">{tooltipMessage}</span>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if errorMessage && $isSmallViewport && !loading}
|
||||
<div class="editor-header mobile" transition:slide={{ duration: 150 }}>
|
||||
@@ -1053,7 +1057,7 @@
|
||||
border-bottom: 1px solid var(--border-neutral);
|
||||
|
||||
&.mobile {
|
||||
background: var(--bgcolor-error);
|
||||
background: var(--bgcolor-error-weak);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
|
||||
+35
-4
@@ -25,6 +25,7 @@
|
||||
import {
|
||||
IconCalendar,
|
||||
IconDotsHorizontal,
|
||||
IconDuplicate,
|
||||
IconFingerPrint
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport';
|
||||
@@ -411,9 +412,10 @@
|
||||
await invalidate(Dependencies.DOCUMENTS);
|
||||
noSqlDocument.update(() => {
|
||||
return {
|
||||
isNew: false,
|
||||
show: false,
|
||||
document: {}
|
||||
isNew: false,
|
||||
document: {},
|
||||
hasDataChanged: false
|
||||
};
|
||||
});
|
||||
|
||||
@@ -514,7 +516,15 @@
|
||||
|
||||
<SpreadsheetContainer
|
||||
bind:this={spreadsheetContainer}
|
||||
bind:showEditorSideSheet={$noSqlDocument.show}>
|
||||
bind:showEditorSideSheet={$noSqlDocument.show}
|
||||
sideSheetOptions={{
|
||||
sideSheetTitle: $noSqlDocument.document?.$id,
|
||||
submit: {
|
||||
text: 'Update',
|
||||
disabled: !$noSqlDocument.hasDataChanged,
|
||||
onClick: async () => await createOrUpdateDocument($noSqlDocument.document)
|
||||
}
|
||||
}}>
|
||||
{#key $spreadsheetRenderKey}
|
||||
<Spreadsheet.Root
|
||||
height="100%"
|
||||
@@ -596,7 +606,7 @@
|
||||
id={document?.$id}
|
||||
virtualItem={item}
|
||||
select={rowSelection}
|
||||
isSelected={$noSqlDocument?.document?.['$id'] === document.$id}>
|
||||
isSelected={$noSqlDocument?.document?.$id === document.$id}>
|
||||
{#each $collectionColumns as { id: columnId } (columnId)}
|
||||
<Spreadsheet.Cell {root} isEditable={false} column={columnId}>
|
||||
{#if columnId === '$id'}
|
||||
@@ -705,9 +715,30 @@
|
||||
isNew={$noSqlDocument.isNew}
|
||||
loading={$noSqlDocument.loading}
|
||||
bind:data={$noSqlDocument.document}
|
||||
showHeaderActions={!$isSmallViewport}
|
||||
onChange={(_, hasDataChanged) => {
|
||||
$noSqlDocument.hasDataChanged = hasDataChanged;
|
||||
}}
|
||||
onSave={async (document) => await createOrUpdateDocument(document)} />
|
||||
{/snippet}
|
||||
|
||||
{#snippet sideSheetHeaderAction()}
|
||||
<Button.Button
|
||||
icon
|
||||
variant="secondary"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
await copy(JSON.stringify($noSqlDocument.document, null, 2));
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Document copied',
|
||||
timeout: 1250
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconDuplicate}></Icon>
|
||||
</Button.Button>
|
||||
{/snippet}
|
||||
|
||||
{#if selectedDocuments.length > 0}
|
||||
<div class="floating-action-bar">
|
||||
<FloatingActionBar>
|
||||
|
||||
+4
-2
@@ -22,16 +22,18 @@ export const sortState = writable<SortState>({
|
||||
|
||||
export const noSqlDocument = writable<{
|
||||
show: boolean;
|
||||
document?: Models.Document | object;
|
||||
document?: Models.Document | (object & { $id?: string });
|
||||
isNew?: boolean;
|
||||
loading?: boolean;
|
||||
documentId?: string /* for loading from a given id */;
|
||||
hasDataChanged?: boolean;
|
||||
}>({
|
||||
show: false,
|
||||
document: null,
|
||||
isNew: false,
|
||||
loading: false,
|
||||
documentId: null
|
||||
documentId: null,
|
||||
hasDataChanged: false
|
||||
});
|
||||
|
||||
export const documentPermissionSheet = writable({
|
||||
|
||||
Reference in New Issue
Block a user