feat: Migrate ESLint to flat config and resolve initial linting errors

Migrated the ESLint configuration to the new flat config format ()
and addressed several initial linting errors.

Key changes include:
- Updated ESLint configuration to treat  as warnings instead of errors.
- Fixed  errors in  by declaring  and .
- Corrected  error in  by using  instead of an out-of-scope .
- Resolved  error in  by replacing the undefined  directive with the  component.
- Addressed  errors in  by replacing  with  and  with .
- Fixed  errors in  by importing necessary modules (, , ) and adding missing props (, , , , ).
This commit is contained in:
Scott Idem
2025-11-17 18:46:54 -05:00
parent b99e85f1db
commit 7e1eaba3bc
374 changed files with 95654 additions and 93952 deletions

View File

@@ -1,476 +1,447 @@
<script lang="ts">
// *** Import Svelte core
import { createEventDispatcher, onMount } from 'svelte';
// *** Import Svelte core
import { createEventDispatcher, onMount } from 'svelte';
// *** Import Aether core variables and functions
import type { key_val } from '$lib/stores/ae_stores';
// import { api } from '$lib/api';
// import { update_ae_obj_id_crud } from '$lib/ae_core/core__crud_generic';
import { update_ae_obj } from '$lib/ae_core/core__crud_generic';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
// *** Import Aether core variables and functions
import type { key_val } from '$lib/stores/ae_stores';
// import { api } from '$lib/api';
// import { update_ae_obj_id_crud } from '$lib/ae_core/core__crud_generic';
import { update_ae_obj } from '$lib/ae_core/core__crud_generic';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
// *** Import Aether core components
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Import Aether core components
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Export/Exposed variables and functions for component
export let log_lvl: number = 0;
export let trigger_patch: any = null;
export let api_cfg: key_val = {'api_crud_super_key': null};
// export let api_crud_super_key: null|string = api_cfg.api_crud_super_key;
export let object_type: string;
export let object_id: string;
export let field_name: string;
export let field_type: string = 'text'; // button, text, textarea, template (older method), select (in progress method)
export let field_value: any;
export let allow_null: boolean = false;
export let select_option_li: key_val = {};
export let val_empty_is_null: boolean = false; // This was added to help with a select option list. If the value is empty (''), it will be set to null.
export let edit_label: string = 'Edit';
export let display_inline: boolean = false;
export let display_block_edit: boolean = false;
// *** Export/Exposed variables and functions for component
export let log_lvl: number = 0;
export let trigger_patch: any = null;
export let api_cfg: key_val = { api_crud_super_key: null };
// export let api_crud_super_key: null|string = api_cfg.api_crud_super_key;
export let object_type: string;
export let object_id: string;
export let field_name: string;
export let field_type: string = 'text'; // button, text, textarea, template (older method), select (in progress method)
export let field_value: any;
export let allow_null: boolean = false;
export let select_option_li: key_val = {};
export let val_empty_is_null: boolean = false; // This was added to help with a select option list. If the value is empty (''), it will be set to null.
export let edit_label: string = 'Edit';
export let display_inline: boolean = false;
export let display_block_edit: boolean = false;
export let textarea_cols: number = 80;
export let textarea_rows: number = 5;
export let textarea_cols: number = 80;
export let textarea_rows: number = 5;
// export let input_field_template: null|object = null;
// export let input_field_template: null|object = null;
export let hide_edit_btn = false;
export let outline_element = false;
export let show_crud = false;
export let btn_label = null; // '<span class="fas fa-check mx-1"></span> Save'; // PATCH
export let hide_edit_btn = false;
export let outline_element = false;
export let show_crud = false;
export let btn_label = null; // '<span class="fas fa-check mx-1"></span> Save'; // PATCH
// export let show_field_name = true;
// export let show_original_value = true;
// export let trim_string = false;
// export let remove_breaks = false;
// export let show_field_name = true;
// export let show_original_value = true;
// export let trim_string = false;
// export let remove_breaks = false;
export let class_li: string = '';
export let class_li: string = '';
// *** Set initial variables
let ae_promises: key_val = {}; // Promise<any>;
let patch_result: null|Promise<any>|key_val|string;
// *** Set initial variables
let ae_promises: key_val = {}; // Promise<any>;
let patch_result: null | Promise<any> | key_val | string;
let original_field_value = field_value;
let original_field_value = field_value;
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher();
onMount(() => {
if (log_lvl) {
console.log(
`Element AE CRUD: Object Type: ${object_type}; Object ID: ${object_id}; Field Name: ${field_name}; Field Value: ${field_value} (Original: ${original_field_value})`
);
// ; Super Key: ${api_crud_super_key}
}
});
onMount(() => {
if (log_lvl) {
console.log(`Element AE CRUD: Object Type: ${object_type}; Object ID: ${object_id}; Field Name: ${field_name}; Field Value: ${field_value} (Original: ${original_field_value})`);
// ; Super Key: ${api_crud_super_key}
}
});
$: if (trigger_patch == true) {
console.log('AE CRUD: Patch triggered!');
trigger_patch = null;
handle_obj_field_patch(field_value);
}
// Updated 2024-03-22
async function handle_obj_field_patch(new_field_value: any) {
console.log('*** handle_obj_field_patch() ***');
$: if (trigger_patch == true) {
console.log('AE CRUD: Patch triggered!');
trigger_patch = null;
handle_obj_field_patch(field_value);
}
patch_result = null;
// This was added to help with a select option list. If the value is empty (''), it will be set to null.
if (val_empty_is_null && new_field_value == '') {
new_field_value = null;
}
// NOTE: Is this needed? The field_name, field_value, and fields parameters for update_ae_obj_id_crud() already take care of the data portion. They are added to data_list object as part of the JSON request.
// NOTE: Currently this only handles one field and value at a time. This may need to be changed in the future to use the "fields" parameter as well.
// let patch_data = {}
// Updated 2024-03-22
async function handle_obj_field_patch(new_field_value: any) {
console.log('*** handle_obj_field_patch() ***');
// if (remove_breaks) {
// patch_data['field_value'] = field_value.replace(/(\r\n|\n|\r)/gm, "");
// } else {
// patch_data['field_value'] = field_value;
// }
patch_result = null;
// patch_data[field_name] = field_value;
// console.log(patch_data);
// This was added to help with a select option list. If the value is empty (''), it will be set to null.
if (val_empty_is_null && new_field_value == '') {
new_field_value = null;
}
// let params = {};
// NOTE: Is this needed? The field_name, field_value, and fields parameters for update_ae_obj_id_crud() already take care of the data portion. They are added to data_list object as part of the JSON request.
// NOTE: Currently this only handles one field and value at a time. This may need to be changed in the future to use the "fields" parameter as well.
// let patch_data = {}
ae_promises.api_update__ae_obj = core_func
.handle_update_ae_obj_id_crud({
api_cfg: api_cfg,
object_type: object_type,
object_id: object_id,
field_name: field_name,
new_field_value: new_field_value,
params: {},
try_cache: false,
log_lvl: 0
})
.then(function (results) {
console.log('Field PATCH Promise', results);
// if (remove_breaks) {
// patch_data['field_value'] = field_value.replace(/(\r\n|\n|\r)/gm, "");
// } else {
// patch_data['field_value'] = field_value;
// }
if (results) {
console.log(
`Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}; Original Field Value: ${original_field_value}`
);
patch_result = 'PATCH complete';
original_field_value = new_field_value;
} else {
console.log(
`Not Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}; Original Field Value: ${original_field_value}`
);
patch_result = 'PATCH failed';
return false;
}
return true;
})
.catch(function (error: any) {
console.log('Something went wrong patching the record.');
console.log(error);
return false;
})
.finally(function () {
console.log('Field PATCH Promise finally');
// This dispatch() must be under "finally".
dispatch('ae_crud_updated', {
type: object_type,
id: object_id,
field_name: field_name,
field_value: new_field_value,
original_value: original_field_value
});
});
// patch_data[field_name] = field_value;
// console.log(patch_data);
// let params = {};
ae_promises.api_update__ae_obj = core_func.handle_update_ae_obj_id_crud({
api_cfg: api_cfg,
object_type: object_type,
object_id: object_id,
field_name: field_name,
new_field_value: new_field_value,
params: {},
try_cache: false,
log_lvl: 0
})
.then(function (results) {
console.log('Field PATCH Promise', results);
if (results) {
console.log(`Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}; Original Field Value: ${original_field_value}`);
patch_result = 'PATCH complete';
original_field_value = new_field_value;
} else {
console.log(`Not Patched - Field Name: ${field_name} with new Field Value: ${new_field_value}; Original Field Value: ${original_field_value}`);
patch_result = 'PATCH failed';
return false;
}
return true;
})
.catch(function (error: any) {
console.log('Something went wrong patching the record.');
console.log(error);
return false;
})
.finally(function () {
console.log('Field PATCH Promise finally');
// This dispatch() must be under "finally".
dispatch(
'ae_crud_updated',
{
'type': object_type,
'id': object_id,
'field_name': field_name,
'field_value': new_field_value,
'original_value': original_field_value,
}
);
});
return ae_promises.api_update__ae_obj;
}
return ae_promises.api_update__ae_obj;
}
</script>
<div class="{class_li} ae_crud" class:show_crud class:display_inline class:outline_element>
<span class="field_viewing_wrapper">
<slot></slot>
<div
class="{class_li} ae_crud"
class:show_crud
class:display_inline
class:outline_element
>
<span class="field_viewing_wrapper">
<slot></slot>
<button
class="btn btn-sm font-normal preset-tonal-warning hover:preset-tonal-error border border-error-500 field_show_btn"
class:hide_edit_btn
class:show_crud
on:dblclick={() => {
show_crud = true;
}}
title="Double click to edit property"
>
<span class="fas fa-edit"></span>
<span class="hidden">Edit</span>
</button>
</span>
<button
class="btn btn-sm font-normal preset-tonal-warning hover:preset-tonal-error border border-error-500 field_show_btn"
class:hide_edit_btn
class:show_crud
on:dblclick={() => {
show_crud = true;
}}
title="Double click to edit property"
>
<span class="fas fa-edit"></span>
<span class="hidden">Edit</span>
</button>
</span>
<div
class:display_block_edit
class="field_editing_wrapper font-normal min-w-content w-100 max-w-full"
>
<!-- <span class="grow flex flex-row items-center justify-between"> -->
<span class="hidden">
{edit_label}
</span>
<div
class:display_block_edit
class="field_editing_wrapper font-normal min-w-content w-100 max-w-full"
>
<!-- <span class="grow flex flex-row items-center justify-between"> -->
<span class="hidden">
{edit_label}
</span>
<button
class="btn btn-md font-normal ae_btn_surface_outlined m-1"
class:show_crud
on:click={() => {
show_crud = false;
}}
title="Close field editing"
>
<span class="fas fa-window-close"></span>
<span class="hidden sm:inline">Close</span>
</button>
<!-- </span> -->
<button
class="btn btn-md font-normal ae_btn_surface_outlined m-1"
class:show_crud
on:click={() => {
show_crud = false;
}}
title="Close field editing"
>
<span class="fas fa-window-close"></span>
<span class="hidden sm:inline">Close</span>
</button>
<!-- </span> -->
<span class="field_value grow min-w-content max-w-full" class:show_crud>
{#if field_type == 'template'}
<!-- <Element_input_v2 {...input_field_template} bind:value={field_value} /> -->
{:else if field_type == 'button'}
<!-- <input type="button" bind:value={field_value}> -->
{field_value}
{:else if field_type == 'select'}
<select bind:value={field_value} class="select">
{#if select_option_li && Object.keys(select_option_li).length == 0}
<option value="">-- not set --</option>
{:else if select_option_li && Object.keys(select_option_li).length > 0}
{#each Object.keys(select_option_li) as option}
<option value={option}>{select_option_li[option]}</option>
{/each}
{:else}
<option value="">-- no list --</option>
{/if}
</select>
{:else if field_type == 'text'}
<input bind:value={field_value} class="input ae_btn_surface min-w-64 w-96 max-w-full" />
{:else if field_type == 'textarea'}
<textarea
bind:value={field_value}
cols={textarea_cols}
rows={textarea_rows}
class="textarea"
></textarea>
{:else}
<input bind:value={field_value} class="input w-fit" />
{/if}
{#if allow_null}
<button
class="btn btn-sm ae_btn_warning m-1"
on:click={() => {
field_value = null;
}}
title="Set value to NULL"
>
Ø NULL
</button>
{/if}
</span>
<span
class="field_value grow min-w-content max-w-full"
class:show_crud
>
{#if field_type == 'template'}
<!-- <Element_input_v2 {...input_field_template} bind:value={field_value} /> -->
{:else if field_type == 'button'}
<!-- <input type="button" bind:value={field_value}> -->
{field_value}
{:else if field_type == 'select'}
<select
bind:value={field_value}
class="select"
>
{#if select_option_li && Object.keys(select_option_li).length == 0}
<option value="">-- not set --</option>
{:else if select_option_li && Object.keys(select_option_li).length > 0}
{#each Object.keys(select_option_li) as option}
<option value={option}>{select_option_li[option]}</option>
{/each}
{:else}
<option value="">-- no list --</option>
{/if}
</select>
<button
class="btn btn-lg ae_btn_warning_outlined m-1"
class:show_crud
disabled={field_value == original_field_value}
on:click={async () => {
handle_obj_field_patch(field_value);
}}
title="Save new field value"
>
{#if field_value == original_field_value}
{#if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-check mx-1"></span>
<span>Save</span>
{/if}
<!-- <span>{patch_result}</span> -->
{:else if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-save mx-1"></span>
<span>Save</span>
{/if}
</button>
{:else if field_type == 'text'}
<input
bind:value={field_value}
class="input ae_btn_surface min-w-64 w-96 max-w-full"
>
{:else if field_type == 'textarea'}
<textarea
bind:value={field_value}
cols={textarea_cols}
rows={textarea_rows}
class="textarea"
></textarea>
{:else}
<input
bind:value={field_value}
class="input w-fit"
>
{/if}
{#if allow_null}
<button
class="btn btn-sm ae_btn_warning m-1"
on:click={() => {
field_value = null;
}}
title="Set value to NULL"
>
Ø
NULL
</button>
{/if}
</span>
<button
class="btn btn-lg ae_btn_warning_outlined m-1"
class:show_crud
disabled={field_value == original_field_value}
on:click={async () => {
handle_obj_field_patch(field_value);
}}
title="Save new field value"
>
{#if field_value == original_field_value}
{#if (btn_label)}
{@html btn_label}
{:else}
<span class="fas fa-check mx-1"></span>
<span>Save</span>
{/if}
<!-- <span>{patch_result}</span> -->
{:else}
{#if (btn_label)}
{@html btn_label}
{:else}
<span class="fas fa-save mx-1"></span>
<span>Save</span>
{/if}
{/if}
</button>
<div class="field_patch_result" class:show_crud>
{#await ae_promises.api_update__ae_obj}
<span class="fas fa-spinner fa-spin mx-1"></span>
<span>Processing...</span>
{:then}
{#if patch_result}
<span class="fas fa-check mx-1"></span>
<span>{patch_result}</span>
{:else}
<!-- <div>Nothing to show yet...</div> -->
{/if}
{/await}
</div>
</div>
<div class="field_patch_result" class:show_crud>
{#await ae_promises.api_update__ae_obj}
<span class="fas fa-spinner fa-spin mx-1"></span>
<span>Processing...</span>
{:then}
{#if patch_result}
<span class="fas fa-check mx-1"></span>
<span>{patch_result}</span>
{:else}
<!-- <div>Nothing to show yet...</div> -->
{/if}
{/await}
</div>
</div>
</div>
<style>
/* .ae_crud .field_editing_wrapper {
/* .ae_crud .field_editing_wrapper {
font-size: 1em;
} */
/* BEGIN: Svelte CRUD (update) component */
/* .ae_crud {
/* BEGIN: Svelte CRUD (update) component */
/* .ae_crud {
margin: 0;
padding: 0;
} */
.ae_crud.display_inline {
/* outline: solid thin red; */
display: inline;
}
.ae_crud.display_inline {
/* outline: solid thin red; */
display: inline;
}
.ae_crud.show_crud .field_viewing_wrapper .field_show_btn {
display: none;
}
.ae_crud.show_crud .field_viewing_wrapper .field_show_btn {
display: none;
}
.ae_crud.outline_element .field_viewing_wrapper {
outline: dotted thin hsla(0,50%,50%,0);
transition: outline 3s 1s;
}
.ae_crud.outline_element .field_viewing_wrapper:hover {
outline: dashed thin hsla(0,50%,50%,.9);
transition: outline 1s .5s;
}
.ae_crud.outline_element .field_viewing_wrapper {
outline: dotted thin hsla(0, 50%, 50%, 0);
transition: outline 3s 1s;
}
.ae_crud.outline_element .field_viewing_wrapper:hover {
outline: dashed thin hsla(0, 50%, 50%, 0.9);
transition: outline 1s 0.5s;
}
.ae_crud .field_viewing_wrapper .field_show_btn.hide_edit_btn {
display: none;
}
.ae_crud .field_viewing_wrapper .field_show_btn.hide_edit_btn {
display: none;
}
.ae_crud .field_viewing_wrapper .field_show_btn {
margin: 0;
padding: 0;
.ae_crud .field_viewing_wrapper .field_show_btn {
margin: 0;
padding: 0;
/* color: hsla(0,0%,50%,.8); */
opacity: .25;
/* color: hsla(0,0%,50%,.8); */
opacity: 0.25;
/* NOTE: transition when hover ends */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: 1.00s; /* no delay */
transition-duration: .55s;
transition-timing-function: linear;
}
/* NOTE: transition when hover ends */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: 1s; /* no delay */
transition-duration: 0.55s;
transition-timing-function: linear;
}
.ae_crud .field_viewing_wrapper:hover .field_show_btn {
/* outline: solid thin hsla(0,50%,50%,.9); */
/* color: hsla(0,50%,50%,.9); */
.ae_crud .field_viewing_wrapper:hover .field_show_btn {
/* outline: solid thin hsla(0,50%,50%,.9); */
/* color: hsla(0,50%,50%,.9); */
opacity: 1;
opacity: 1;
/* NOTE: transition when hover starts */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: .25s; /* no delay */
transition-duration: .50s;
transition-timing-function: linear;
}
/* NOTE: transition when hover starts */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: 0.25s; /* no delay */
transition-duration: 0.5s;
transition-timing-function: linear;
}
.ae_crud .field_editing_wrapper {
/* outline: dashed thin pink; */
.ae_crud .field_editing_wrapper {
/* outline: dashed thin pink; */
display: none;
/* position: relative; */
display: none;
/* position: relative; */
/* contain: layout; */
/* contain: size; */
/* contain: none; */
contain: content;
/* contain: layout; */
/* contain: size; */
/* contain: none; */
contain: content;
box-shadow: .5em .5em .75em .75em hsla(0,0%,0%,0.5);
box-shadow: 0.5em 0.5em 0.75em 0.75em hsla(0, 0%, 0%, 0.5);
/* color: hsla(0,0%,50%,.8); */
background-color: hsla(0,0%,50%,.5);
border: dashed thin transparent;
/* color: hsla(0,0%,50%,.8); */
background-color: hsla(0, 0%, 50%, 0.5);
border: dashed thin transparent;
/* font-size: 1em; */
/* line-height: 1em; */
/* font-size: 1em; */
/* line-height: 1em; */
height: 1em;
width: 1em;
height: 1em;
width: 1em;
/* NOTE: transition when hover ends */
transition-property: background-color, border-color, color, height, width;
transition-delay: .75s; /* short delay */
transition-duration: .50s;
transition-timing-function: linear;
/* NOTE: transition when hover ends */
transition-property: background-color, border-color, color, height, width;
transition-delay: 0.75s; /* short delay */
transition-duration: 0.5s;
transition-timing-function: linear;
}
}
.ae_crud.show_crud .field_editing_wrapper {
/* display: initial; */
display: block;
contain: content;
/* background-color: yellow; */
background-color: hsla(60, 50%, 80%, 0.95);
border: solid thin hsla(0, 50%, 50%, 0.5);
border-radius: 0.5em;
height: auto;
/* height: 100%; */
max-height: 100%;
.ae_crud.show_crud .field_editing_wrapper {
/* display: initial; */
display: block;
contain: content;
min-width: fit-content;
width: auto;
max-width: 100%;
/* background-color: yellow; */
background-color: hsla(60,50%,80%,.95);
border: solid thin hsla(0,50%,50%,.50);
border-radius: .5em;
/* NOTE: transition when hover starts */
transition-property: background-color, border-color, height, width;
transition-delay: 0.25s; /* no delay */
transition-duration: 0.25s;
transition-timing-function: linear;
height: auto;
/* height: 100%; */
max-height: 100%;
/* position: absolute; */
/* position: fixed; */
/* position: relative; */
/* top: 1em; */
/* left: 1em; */
/* right: 1em; */
/* bottom: 1em; */
min-width: fit-content;
width: auto;
max-width: 100%;
z-index: 1;
/* NOTE: transition when hover starts */
transition-property: background-color, border-color, height, width;
transition-delay: .25s; /* no delay */
transition-duration: .25s;
transition-timing-function: linear;
padding: 0.5em;
}
/* position: absolute; */
/* position: fixed; */
/* position: relative; */
/* top: 1em; */
/* left: 1em; */
/* right: 1em; */
/* bottom: 1em; */
.ae_crud.show_crud.display_inline .field_editing_wrapper:hover {
background-color: hsla(60, 60%, 90%, 0.8);
z-index: 55;
}
z-index: 1;
.ae_crud.show_crud.display_inline .field_editing_wrapper {
/* display: block; */
display: inline-block;
/* display: inline; */
padding: .5em;
}
box-shadow: initial;
background-color: hsla(60, 50%, 80%, 0.4);
padding: 0.25em;
.ae_crud.show_crud.display_inline .field_editing_wrapper:hover {
background-color: hsla(60,60%,90%,.80);
z-index: 55;
}
position: initial;
.ae_crud.show_crud.display_inline .field_editing_wrapper {
/* display: block; */
display: inline-block;
/* display: inline; */
z-index: 0;
box-shadow: initial;
background-color: hsla(60,50%,80%,.40);
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5em;
padding: .25em;
width: 30%;
}
position: initial;
.ae_crud.show_crud.display_inline .field_editing_wrapper.display_block_edit {
/* display: block; */
z-index: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5em;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: .5em;
flex-grow: 1;
width: 30%;
}
height: auto;
width: 100%;
}
.ae_crud.show_crud.display_inline .field_editing_wrapper.display_block_edit {
/* display: block; */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: .5em;
flex-grow: 1;
height: auto;
width: 100%;
}
.ae_crud textarea {
height: auto;
}
/* END: Svelte CRUD (update) component */
.ae_crud textarea {
height: auto;
}
/* END: Svelte CRUD (update) component */
</style>

View File

@@ -1,259 +1,258 @@
<script lang="ts">
interface Props {
log_lvl?: number;
api_cfg?: key_val;
// export let api_crud_super_key: null|string = api_cfg.api_crud_super_key;
interface Props {
log_lvl?: number;
api_cfg?: key_val;
// export let api_crud_super_key: null|string = api_cfg.api_crud_super_key;
trigger_patch?: any;
patch_status?: string; // '', processing, complete, error
patch_complete?: boolean|null|string; // null = not started, true = complete/success, false = error
patch_result?: boolean|null|key_val; // Result of the patch operation
trigger_patch?: any;
patch_status?: string; // '', processing, complete, error
patch_complete?: boolean | null | string; // null = not started, true = complete/success, false = error
patch_result?: boolean | null | key_val; // Result of the patch operation
object_type: string;
object_id: string;
object_reload?: boolean;
object_type: string;
object_id: string;
object_reload?: boolean;
field_name: string;
field_type?: string; // button, text, textarea, template (older method), select (in progress method)
field_name: string;
field_type?: string; // button, text, textarea, template (older method), select (in progress method)
previous_field_value?: any;
current_field_value?: any;
new_field_value?: any;
previous_field_value?: any;
current_field_value?: any;
new_field_value?: any;
allow_null?: boolean;
select_option_kv?: key_val;
val_empty_is_null?: boolean; // This was added to help with a select option list. If the value is empty (''), it will be set to null.
allow_null?: boolean;
select_option_kv?: key_val;
val_empty_is_null?: boolean; // This was added to help with a select option list. If the value is empty (''), it will be set to null.
edit_label?: string;
display_block?: boolean; // If true, the element will be displayed as a block element.
// display_inline?: boolean;
display_absolute_edit?: boolean; // If true, the edit form will be displayed in the top right corner of the element.
// display_block_edit?: boolean;
textarea_cols?: number;
textarea_rows?: number;
edit_label?: string;
display_block?: boolean; // If true, the element will be displayed as a block element.
// display_inline?: boolean;
display_absolute_edit?: boolean; // If true, the edit form will be displayed in the top right corner of the element.
// display_block_edit?: boolean;
textarea_cols?: number;
textarea_rows?: number;
// export let input_field_template: null|object = null;
// export let input_field_template: null|object = null;
hide_element?: boolean; // This hides the entire custom element.
hide_edit_btn?: boolean; // This only hides the edit button.
// hide_edit_form?: boolean;
show_edit_form?: boolean; // This is used if the parent needs to make the edit form visible.
hide_element?: boolean; // This hides the entire custom element.
hide_edit_btn?: boolean; // This only hides the edit button.
// hide_edit_form?: boolean;
show_edit_form?: boolean; // This is used if the parent needs to make the edit form visible.
outline_element?: boolean;
outline_element?: boolean;
btn_label?: any; // '<span class="fas fa-check mx-1"></span> Save'; // PATCH
// export let remove_breaks = false;
class_li?: string;
children?: import('svelte').Snippet;
}
btn_label?: any; // '<span class="fas fa-check mx-1"></span> Save'; // PATCH
// export let remove_breaks = false;
class_li?: string;
children?: import('svelte').Snippet;
}
let {
log_lvl = 0,
api_cfg = {'api_crud_super_key': null},
let {
log_lvl = 0,
api_cfg = { api_crud_super_key: null },
trigger_patch = null,
patch_status = '',
patch_complete = $bindable(null), // null = not started, true = complete/success, false = error
patch_result = null,
trigger_patch = null,
patch_status = '',
patch_complete = $bindable(null), // null = not started, true = complete/success, false = error
patch_result = null,
object_type,
object_id,
object_reload = false,
object_type,
object_id,
object_reload = false,
field_name,
field_type = 'text',
field_name,
field_type = 'text',
previous_field_value = $bindable(null),
current_field_value = null,
new_field_value = current_field_value,
previous_field_value = $bindable(null),
current_field_value = null,
new_field_value = current_field_value,
allow_null = false,
select_option_kv = $bindable({}),
val_empty_is_null = false, // If the value is empty (''), it will be set to null.
allow_null = false,
select_option_kv = $bindable({}),
val_empty_is_null = false, // If the value is empty (''), it will be set to null.
edit_label = 'Edit',
display_block = false,
// display_inline = false,
display_absolute_edit = false,
// display_block_edit = false,
textarea_cols = 80,
textarea_rows = 5,
edit_label = 'Edit',
display_block = false,
// display_inline = false,
display_absolute_edit = false,
// display_block_edit = false,
textarea_cols = 80,
textarea_rows = 5,
hide_element = $bindable(false),
hide_edit_btn = false,
// hide_edit_form = $bindable(true),
show_edit_form = $bindable(),
hide_element = $bindable(false),
hide_edit_btn = false,
// hide_edit_form = $bindable(true),
show_edit_form = $bindable(),
outline_element = $bindable(false),
outline_element = $bindable(false),
btn_label = null,
class_li = '',
children
}: Props = $props();
btn_label = null,
class_li = '',
children
}: Props = $props();
// *** Import Svelte core
import { browser } from '$app/environment';
// *** Import Svelte core
import { browser } from '$app/environment';
// *** Import Aether core variables and functions
import type { key_val } from '$lib/stores/ae_stores';
// import { api } from '$lib/api';
import { core_func } from '$lib/ae_core/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
// *** Import Aether core variables and functions
import type { key_val } from '$lib/stores/ae_stores';
// import { api } from '$lib/api';
import { core_func } from '$lib/ae_core/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
// *** Import Aether core components
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Import Aether core components
// *** Import Aether module variables and functions
// *** Import Aether module components
// export let show_field_name = true;
// export let show_original_value = true;
// export let trim_string = false;
// export let show_field_name = true;
// export let show_original_value = true;
// export let trim_string = false;
// *** Set initial variables
let ae_promises: key_val = $state({}); // Promise<any>;
// let patch_result: null|Promise<any>|key_val|string = $state();
// *** Set initial variables
let ae_promises: key_val = $state({}); // Promise<any>;
// let patch_result: null|Promise<any>|key_val|string = $state();
$effect(() => {
if (browser) {
if (log_lvl) {
console.log(
`Element AE CRUD: Object Type: ${object_type}; Object ID: ${object_id}; Field Name: ${field_name}; New Field Value: ${new_field_value} (Current: ${current_field_value})`
);
// ; Super Key: ${api_crud_super_key}
}
}
// if (select_option_kv) {
// console.log(select_option_kv);
// }
$effect(() => {
if (browser) {
if (log_lvl) {
console.log(`Element AE CRUD: Object Type: ${object_type}; Object ID: ${object_id}; Field Name: ${field_name}; New Field Value: ${new_field_value} (Current: ${current_field_value})`);
// ; Super Key: ${api_crud_super_key}
}
if (trigger_patch === true) {
if (log_lvl) {
console.log('AE CRUD: Patch triggered by trigger_patch = true');
}
trigger_patch = null;
handle_obj_field_patch(new_field_value);
}
// if (new_field_value !== current_field_value) {
// if (log_lvl > 1) {
// console.log(`AE CRUD: field_value changed from ${current_field_value} to ${new_field_value}`);
// }
// }
}
});
// if (select_option_kv) {
// console.log(select_option_kv);
// }
// Updated 2024-03-22
async function handle_obj_field_patch(new_field_value: any) {
if (log_lvl) {
console.log(
`AE CRUD: Patching Object Type: ${object_type}; Object ID: ${object_id}; Field Name: ${field_name}; New Field Value: ${new_field_value}; Current Field Value: ${current_field_value}`
);
// ; Super Key: ${api_crud_super_key}
}
if (trigger_patch === true) {
if (log_lvl) {
console.log('AE CRUD: Patch triggered by trigger_patch = true');
}
trigger_patch = null;
handle_obj_field_patch(new_field_value);
patch_status = 'Processing...';
patch_complete = null;
patch_result = null;
// if (new_field_value !== current_field_value) {
// if (log_lvl > 1) {
// console.log(`AE CRUD: field_value changed from ${current_field_value} to ${new_field_value}`);
// }
// }
}
});
// This was added to help with a select option list. If the value is empty (''), it will be set to null.
if (val_empty_is_null && new_field_value == '') {
new_field_value = null;
}
// NOTE: Is this needed? The field_name, field_value, and fields parameters for update_ae_obj_id_crud() already take care of the data portion. They are added to data_list object as part of the JSON request.
// NOTE: Currently this only handles one field and value at a time. This may need to be changed in the future to use the "fields" parameter as well.
// let patch_data = {}
// Updated 2024-03-22
async function handle_obj_field_patch(new_field_value: any) {
if (log_lvl) {
console.log(`AE CRUD: Patching Object Type: ${object_type}; Object ID: ${object_id}; Field Name: ${field_name}; New Field Value: ${new_field_value}; Current Field Value: ${current_field_value}`);
// ; Super Key: ${api_crud_super_key}
}
// if (remove_breaks) {
// patch_data['field_value'] = field_value.replace(/(\r\n|\n|\r)/gm, "");
// } else {
// patch_data['field_value'] = field_value;
// }
patch_status = 'Processing...';
patch_complete = null;
patch_result = null;
// patch_data[field_name] = field_value;
// console.log(patch_data);
// This was added to help with a select option list. If the value is empty (''), it will be set to null.
if (val_empty_is_null && new_field_value == '') {
new_field_value = null;
}
// let params = {};
// NOTE: Is this needed? The field_name, field_value, and fields parameters for update_ae_obj_id_crud() already take care of the data portion. They are added to data_list object as part of the JSON request.
// NOTE: Currently this only handles one field and value at a time. This may need to be changed in the future to use the "fields" parameter as well.
// let patch_data = {}
// if (remove_breaks) {
// patch_data['field_value'] = field_value.replace(/(\r\n|\n|\r)/gm, "");
// } else {
// patch_data['field_value'] = field_value;
// }
// patch_data[field_name] = field_value;
// console.log(patch_data);
// let params = {};
ae_promises.api_update__ae_obj = core_func.update_ae_obj_id_crud_v2({
api_cfg: api_cfg,
object_type: object_type,
object_id: object_id,
object_reload: object_reload,
field_name: field_name,
new_field_value: new_field_value,
log_lvl: log_lvl
})
.then(function (results) {
// console.log('Field PATCH Promise', results);
if (results) {
// console.log(`Patched - Field Name: ${field_name} with New Field Value: ${new_field_value}; Original Field Value: ${current_field_value}`);
patch_status = 'PATCH complete';
current_field_value = new_field_value;
} else {
console.log(`Not Patched - Field Name: ${field_name} with New Field Value: ${new_field_value}; Original Field Value: ${current_field_value}`);
patch_status = 'PATCH failed';
return false;
}
return true;
})
.catch(function (error: any) {
patch_status = 'Error during PATCH';
console.log('Something went wrong patching the record.');
console.log(error);
return false;
})
.finally(function () {
// console.log('Field PATCH Promise finally');
if (patch_status != 'PATCH complete') {
patch_status = 'Error during PATCH';
patch_complete = false;
patch_result = false;
} else {
// patch_complete = true;
patch_complete = object_id; // Return the object ID that was patched
patch_result = {
'type': object_type,
'id': object_id,
'field_name': field_name,
'field_value': new_field_value,
'original_value': current_field_value,
}
}
});
return ae_promises.api_update__ae_obj;
}
ae_promises.api_update__ae_obj = core_func
.update_ae_obj_id_crud_v2({
api_cfg: api_cfg,
object_type: object_type,
object_id: object_id,
object_reload: object_reload,
field_name: field_name,
new_field_value: new_field_value,
log_lvl: log_lvl
})
.then(function (results) {
// console.log('Field PATCH Promise', results);
if (results) {
// console.log(`Patched - Field Name: ${field_name} with New Field Value: ${new_field_value}; Original Field Value: ${current_field_value}`);
patch_status = 'PATCH complete';
current_field_value = new_field_value;
} else {
console.log(
`Not Patched - Field Name: ${field_name} with New Field Value: ${new_field_value}; Original Field Value: ${current_field_value}`
);
patch_status = 'PATCH failed';
return false;
}
return true;
})
.catch(function (error: any) {
patch_status = 'Error during PATCH';
console.log('Something went wrong patching the record.');
console.log(error);
return false;
})
.finally(function () {
// console.log('Field PATCH Promise finally');
if (patch_status != 'PATCH complete') {
patch_status = 'Error during PATCH';
patch_complete = false;
patch_result = false;
} else {
// patch_complete = true;
patch_complete = object_id; // Return the object ID that was patched
patch_result = {
type: object_type,
id: object_id,
field_name: field_name,
field_value: new_field_value,
original_value: current_field_value
};
}
});
return ae_promises.api_update__ae_obj;
}
</script>
<!-- relative -->
<div
class="{class_li} ae_crud
class="{class_li} ae_crud
font-normal
transition-all duration-300 ease-in-out
relative
"
class:hidden={hide_element}
class:block={display_block}
class:inline-block={!display_block}
class:outline_element
>
class:hidden={hide_element}
class:block={display_block}
class:inline-block={!display_block}
class:outline_element
>
{@render children?.()}
{@render children?.()}
<span
class="field_viewing_wrapper
<span
class="field_viewing_wrapper
"
>
<!-- {@render children?.()} -->
>
<!-- {@render children?.()} -->
<button
type="button"
class:hidden={show_edit_form || hide_edit_btn}
class="
<button
type="button"
class:hidden={show_edit_form || hide_edit_btn}
class="
btn btn-icon
text-xs
m-0 px-0.5
@@ -262,28 +261,26 @@ async function handle_obj_field_patch(new_field_value: any) {
opacity-50 hover:opacity-100
transition-all
"
ondblclick={() => {
// hide_edit_btn = true;
// hide_edit_form = false;
show_edit_form = true;
}}
title="Double click to edit property"
>
<span class="fas fa-edit m-0 p-0"></span>
<span class="hidden">Edit</span>
</button>
</span>
ondblclick={() => {
// hide_edit_btn = true;
// hide_edit_form = false;
show_edit_form = true;
}}
title="Double click to edit property"
>
<span class="fas fa-edit m-0 p-0"></span>
<span class="hidden">Edit</span>
</button>
</span>
<!-- class:display_block={display_block} -->
<div
class:hidden={!show_edit_form}
class:absolute={display_absolute_edit}
class:top-0={display_absolute_edit}
class:right-0={display_absolute_edit}
class:drop-shadow-2xl={display_absolute_edit}
class="field_editing_wrapper_v2
<!-- class:display_block={display_block} -->
<div
class:hidden={!show_edit_form}
class:absolute={display_absolute_edit}
class:top-0={display_absolute_edit}
class:right-0={display_absolute_edit}
class:drop-shadow-2xl={display_absolute_edit}
class="field_editing_wrapper_v2
border-2 border-dashed hover:border-solid
border-pink-400 dark:border-pink-600
rounded-md
@@ -298,282 +295,257 @@ async function handle_obj_field_patch(new_field_value: any) {
space-y-2
transition-all duration-300 ease-in-out
"
>
<span class="grow flex flex-row items-center justify-between">
<span class="hidden md:inline font-semibold text-lg">
{edit_label}
</span>
>
<span class="grow flex flex-row items-center justify-between">
<span class="hidden md:inline font-semibold text-lg">
{edit_label}
</span>
<button
type="button"
class="btn btn-md ae_btn_surface_outlined m-1"
onclick={() => {
// hide_edit_btn = false;
// hide_edit_form = true;
show_edit_form = false;
}}
title="Close field editing. This does not save any changes."
>
<span class="fas fa-window-close"></span>
<span class="hidden sm:inline">Close</span>
</button>
</span>
<button
type="button"
class="btn btn-md ae_btn_surface_outlined m-1"
onclick={() => {
// hide_edit_btn = false;
// hide_edit_form = true;
show_edit_form = false;
}}
title="Close field editing. This does not save any changes."
>
<span class="fas fa-window-close"></span>
<span class="hidden sm:inline">Close</span>
</button>
</span>
<span>
New value: {new_field_value}
</span>
<span>
New value: {new_field_value}
</span>
<span
class="field_value
<span
class="field_value
grow
min-w-fit w-full max-w-fit
flex flex-row flex-wrap items-center justify-center gap-1
"
>
{#if field_type == 'template'}
<!-- <Element_input_v2 {...input_field_template} bind:value={field_value} /> -->
{:else if field_type == 'button'}
<!-- <input type="button" bind:value={field_value}> -->
{new_field_value}
{:else if field_type == 'select'}
<select
bind:value={new_field_value}
class="
>
{#if field_type == 'template'}
<!-- <Element_input_v2 {...input_field_template} bind:value={field_value} /> -->
{:else if field_type == 'button'}
<!-- <input type="button" bind:value={field_value}> -->
{new_field_value}
{:else if field_type == 'select'}
<select
bind:value={new_field_value}
class="
select
p-1
min-w-fit w-full
border border-gray-300 dark:border-gray-600
"
>
{#if select_option_kv && Object.keys(select_option_kv).length == 0}
<option value="">-- not set --</option>
{:else if select_option_kv && Object.keys(select_option_kv).length > 0}
{#each Object.keys(select_option_kv) as option}
<option value={option}>{select_option_kv[option]} {option}</option>
{/each}
{:else}
<option value="">-- no list --</option>
{/if}
</select>
>
{#if select_option_kv && Object.keys(select_option_kv).length == 0}
<option value="">-- not set --</option>
{:else if select_option_kv && Object.keys(select_option_kv).length > 0}
{#each Object.keys(select_option_kv) as option}
<option value={option}>{select_option_kv[option]} {option}</option>
{/each}
{:else}
<option value="">-- no list --</option>
{/if}
</select>
{:else if field_type == 'text'}
<input bind:value={new_field_value} class="input ae_btn_surface min-w-64 w-96 max-w-full" />
{:else if field_type == 'textarea'}
<textarea
bind:value={new_field_value}
cols={textarea_cols}
rows={textarea_rows}
class="textarea"
></textarea>
{:else}
<input bind:value={new_field_value} class="input w-fit" />
{/if}
{#if allow_null}
<button
class="btn btn-sm ae_btn_warning m-1"
onclick={() => {
new_field_value = null;
}}
title="Set value to NULL"
>
Ø NULL
</button>
{/if}
</span>
{:else if field_type == 'text'}
<input
bind:value={new_field_value}
class="input ae_btn_surface min-w-64 w-96 max-w-full"
>
{:else if field_type == 'textarea'}
<textarea
bind:value={new_field_value}
cols={textarea_cols}
rows={textarea_rows}
class="textarea"
></textarea>
{:else}
<input
bind:value={new_field_value}
class="input w-fit"
>
{/if}
{#if allow_null}
<button
class="btn btn-sm ae_btn_warning m-1"
onclick={() => {
new_field_value = null;
}}
title="Set value to NULL"
>
Ø
NULL
</button>
{/if}
</span>
<button
class="
<button
class="
ae_btn_warning_outlined
btn btn-lg
m-1
"
disabled={new_field_value === current_field_value}
onclick={async () => {
handle_obj_field_patch(new_field_value);
}}
title="Save new field value"
>
{#if new_field_value === current_field_value}
{#if (btn_label)}
{@html btn_label}
{:else}
<span class="fas fa-check mx-1"></span>
<span>Save</span>
{/if}
<!-- <span>{patch_result}</span> -->
{:else}
{#if (btn_label)}
{@html btn_label}
{:else}
<span class="fas fa-save mx-1"></span>
<span>Save</span>
{/if}
{/if}
</button>
<div
class="field_patch_result"
class:hidden={!patch_status}
>
{#await ae_promises.api_update__ae_obj}
<span class="fas fa-spinner fa-spin mx-1"></span>
<span>Processing...</span>
{:then}
{#if patch_status}
<span class="fas fa-check mx-1"></span>
<span>{patch_status}</span>
{:else}
<!-- <div>Nothing to show yet...</div> -->
{/if}
{/await}
</div>
</div>
disabled={new_field_value === current_field_value}
onclick={async () => {
handle_obj_field_patch(new_field_value);
}}
title="Save new field value"
>
{#if new_field_value === current_field_value}
{#if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-check mx-1"></span>
<span>Save</span>
{/if}
<!-- <span>{patch_result}</span> -->
{:else if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-save mx-1"></span>
<span>Save</span>
{/if}
</button>
<div class="field_patch_result" class:hidden={!patch_status}>
{#await ae_promises.api_update__ae_obj}
<span class="fas fa-spinner fa-spin mx-1"></span>
<span>Processing...</span>
{:then}
{#if patch_status}
<span class="fas fa-check mx-1"></span>
<span>{patch_status}</span>
{:else}
<!-- <div>Nothing to show yet...</div> -->
{/if}
{/await}
</div>
</div>
</div>
<style>
.ae_crud.outline_element .field_viewing_wrapper {
outline: dotted thin hsla(0, 50%, 50%, 0);
transition: outline 3s 1s;
}
.ae_crud.outline_element .field_viewing_wrapper:hover {
outline: dashed thin hsla(0, 50%, 50%, 0.9);
transition: outline 1s 0.5s;
}
.ae_crud.outline_element .field_viewing_wrapper {
outline: dotted thin hsla(0,50%,50%,0);
transition: outline 3s 1s;
}
.ae_crud.outline_element .field_viewing_wrapper:hover {
outline: dashed thin hsla(0,50%,50%,.9);
transition: outline 1s .5s;
}
.ae_crud .field_viewing_wrapper {
margin: 0;
padding: 0;
/* color: hsla(0,0%,50%,.8); */
opacity: 0.25;
/* NOTE: transition when hover ends */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: 1s; /* no delay */
transition-duration: 0.55s;
transition-timing-function: linear;
}
.ae_crud .field_viewing_wrapper {
margin: 0;
padding: 0;
.ae_crud .field_viewing_wrapper:hover {
/* outline: solid thin hsla(0,50%,50%,.9); */
/* color: hsla(0,50%,50%,.9); */
/* color: hsla(0,0%,50%,.8); */
opacity: .25;
opacity: 1;
/* NOTE: transition when hover ends */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: 1.00s; /* no delay */
transition-duration: .55s;
transition-timing-function: linear;
}
/* NOTE: transition when hover starts */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: 0.25s; /* no delay */
transition-duration: 0.5s;
transition-timing-function: linear;
}
.ae_crud .field_viewing_wrapper:hover {
/* outline: solid thin hsla(0,50%,50%,.9); */
/* color: hsla(0,50%,50%,.9); */
.ae_crud .field_editing_wrapper {
/* outline: dashed thin pink; */
opacity: 1;
/* position: relative; */
/* NOTE: transition when hover starts */
transition-property: opacity, background-color, border-color, color, height, width;
transition-delay: .25s; /* no delay */
transition-duration: .50s;
transition-timing-function: linear;
}
/* contain: layout; */
/* contain: size; */
/* contain: none; */
contain: content;
.ae_crud .field_editing_wrapper {
/* outline: dashed thin pink; */
box-shadow: 0.5em 0.5em 0.75em 0.75em hsla(0, 0%, 0%, 0.5);
/* color: hsla(0,0%,50%,.8); */
/* background-color: hsla(0,0%,50%,.5); */
/* border: dashed thin transparent; */
/* position: relative; */
/* background-color: yellow; */
background-color: hsla(60, 50%, 80%, 0.95);
border: solid thin hsla(0, 50%, 50%, 0.5);
border-radius: 0.5em;
/* contain: layout; */
/* contain: size; */
/* contain: none; */
contain: content;
height: auto;
/* height: 100%; */
max-height: 100%;
box-shadow: .5em .5em .75em .75em hsla(0,0%,0%,0.5);
min-width: fit-content;
width: auto;
max-width: 100%;
/* color: hsla(0,0%,50%,.8); */
/* background-color: hsla(0,0%,50%,.5); */
/* border: dashed thin transparent; */
/* font-size: 1em; */
/* line-height: 1em; */
/* background-color: yellow; */
background-color: hsla(60,50%,80%,.95);
border: solid thin hsla(0,50%,50%,.50);
border-radius: .5em;
/* NOTE: transition when hover ends */
transition-property: background-color, border-color, color, height, width;
transition-delay: 0.75s; /* short delay */
transition-duration: 0.5s;
transition-timing-function: linear;
height: auto;
/* height: 100%; */
max-height: 100%;
z-index: 1;
min-width: fit-content;
width: auto;
max-width: 100%;
padding: 0.5em;
}
/* font-size: 1em; */
/* line-height: 1em; */
.ae_crud.show_crud.display_inline .field_editing_wrapper:hover {
background-color: hsla(60, 60%, 90%, 0.8);
z-index: 55;
}
/* NOTE: transition when hover ends */
transition-property: background-color, border-color, color, height, width;
transition-delay: .75s; /* short delay */
transition-duration: .50s;
transition-timing-function: linear;
.ae_crud.show_crud.display_inline .field_editing_wrapper {
/* display: block; */
display: inline-block;
/* display: inline; */
z-index: 1;
box-shadow: initial;
background-color: hsla(60, 50%, 80%, 0.4);
padding: .5em;
padding: 0.25em;
}
position: initial;
z-index: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5em;
width: 30%;
}
.ae_crud.show_crud.display_inline .field_editing_wrapper:hover {
background-color: hsla(60,60%,90%,.80);
z-index: 55;
}
.ae_crud.show_crud.display_inline .field_editing_wrapper.display_block_edit {
/* display: block; */
.ae_crud.show_crud.display_inline .field_editing_wrapper {
/* display: block; */
display: inline-block;
/* display: inline; */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5em;
box-shadow: initial;
background-color: hsla(60,50%,80%,.40);
flex-grow: 1;
padding: .25em;
height: auto;
width: 100%;
}
position: initial;
z-index: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: .5em;
width: 30%;
}
.ae_crud.show_crud.display_inline .field_editing_wrapper.display_block_edit {
/* display: block; */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: .5em;
flex-grow: 1;
height: auto;
width: 100%;
}
.ae_crud textarea {
height: auto;
}
/* END: Svelte CRUD (update) component */
.ae_crud textarea {
height: auto;
}
/* END: Svelte CRUD (update) component */
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,414 +1,426 @@
<script lang="ts">
import { createEventDispatcher, onMount, tick } from 'svelte';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
export let element_id = 'svelte_input_file_element';
export let input_name = 'file_list';
export let container_class_li: string[] = [];
export let input_class_li: string[] = ['file_drop_area'];
export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', '' , 'text-sm'];
export let multiple: boolean = true;
export let required: boolean = true;
export let accept: string = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh';
export let untrusted_extension_list = ['bin', 'dmg', 'exe', 'js', 'msi', 'php', 'py', 'sh'];
export let legacy_extension_list = ['avi', 'doc', 'ppt', 'xls', 'wmv'];
export let use_selected_file_table = true;
export let file_list_status: null|string = null;
export let processed_file_list: any[] = [];
const dispatch = createEventDispatcher();
export let input_file_list: any = null;
let input_file_list_processed: any[] = [];
onMount(() => {
console.log('** Element Mounted: ** Element Input File');
});
$: if (input_file_list) {
console.log(input_file_list);
process_file_list(input_file_list)
.then(function (result) {
// console.log(result);
if (!result || !result.length) {
file_list_status = 'none';
}
// Save the results to the file upload list to be displayed as a table.
input_file_list_processed = result; // Includes file hash
dispatch(
'input_file_list_updated',
{
element_id: element_id,
input_file_list: input_file_list,
input_file_list_processed: result, // Includes file hash
}
);
});
}
async function process_file_list(file_list) {
console.log('*** process_file_list() ***');
file_list_status = 'processing';
processed_file_list = [];
if (!file_list) {
// file_list_processed = null;
file_list_status = 'none';
// await tick();
return processed_file_list;
}
// const forLoop = async _ => {
// console.log('*** Start ***');
// for (let [i, file_item] of file_list.entries()) { // Not sure why this does not work???
for await (const [i, file_item] of Array.prototype.entries.call(file_list)) {
console.log(i, file_item);
// NOTE: The file list is readonly. The filenames can not be changed here.
if (file_item.name.endsWith('.odpmac') || file_item.name.endsWith('.odpwin') || file_item.name.endsWith('.pptmac') || file_item.name.endsWith('.pptwin') || file_item.name.endsWith('.pptxmac') || file_item.name.endsWith('.pptxwin')) {
console.log('This file extension may need to be fixed? API upload will take care of it.');
// file_item.name = file_item.name.replace('.odpwin', '.odp');
}
let file_data: key_val = {};
let filename = file_item.name;
// console.log(filename);
file_data['filename'] = filename;
let guessed_extension = ae_util.guess_file_extension(filename);
file_data['guessed_extension'] = guessed_extension;
file_data['type'] = file_item.type;
let modified_date = new Date(file_item.lastModified);
file_data['modified_date'] = modified_date;
let modified_datetime_string = ae_util.iso_datetime_formatter(modified_date, 'datetime_medium');
file_data['modified_datetime_string'] = modified_datetime_string;
let file_size_bytes = file_item.size;
file_data['file_size_bytes'] = file_size_bytes;
let file_size_string = ae_util.format_bytes(file_item.size, 2);
file_data['file_size_string'] = file_size_string;
// // NOTE: Calculate the hash of the file before upload. Check if this exact file has already been uploaded.
// let file_reader = new FileReader();
// file_reader.onload = async function() {
// const hash_buffer = crypto.subtle.digest('SHA-256', file_reader.result);
// let hash_hex_test = hash_buffer.then(async function (result_buffer) {
// const hash_array = Array.from(new Uint8Array(result_buffer)); // convert buffer to byte array
// const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// console.log(`File hash hex? ${hash_hex}`);
// file_data['hash_sha256'] = hash_hex;
// return hash_hex;
// })
// .catch(function (error: any) {
// console.log('Something went wrong?', error);
// });
// return hash_hex_test;
// // file_data['hash_hex_test'] = hash_hex_test;
// // const hash_buffer = await crypto.subtle.digest('SHA-256', file_reader.result);
// // let hash_str = await new TextDecoder().decode(hash_buffer);
// // console.log(`File hash string? ${hash_str}`);
// // file_data['hash_str'] = hash_str;
// // const hash_array = Array.from(new Uint8Array(hash_buffer)); // convert buffer to byte array
// // const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// // console.log(`File hash hex? ${hash_hex}`);
// // file_data['hash_sha256'] = hash_hex;
// // return hash_hex_test;
// // return hash_hex;
// }
// // file_reader.then(function (result) {
// // console.log(`File hash hex? ${result}`);
// // return hash_hex;
// // })
// // .catch(function (error: any) {
// // console.log('No results returned or failed.', error);
// // });
// // file_data['hash_sha256'] = file_reader.readAsArrayBuffer(file_item);
let warning_untrusted_extension = false;
let warning_legacy_extension = false;
let warning_size = false;
let warning_message = null;
if (untrusted_extension_list.includes(guessed_extension)) {
console.log('This is an untrusted extension. Going to warn.')
warning_untrusted_extension = true;
warning_message = 'It appears this an untrusted file type and is likely meant to be an executable or installable application. It is <strong>strongly</strong> recommended that this file not be used.';
} else if (legacy_extension_list.includes(guessed_extension)) {
console.log('This is a legacy extension. Going to warn.')
warning_legacy_extension = true;
if (guessed_extension == 'ppt') {
warning_message = 'It appears this is a legacy PowerPoint file and has not been officially supported since Office PowerPoint 2003. This file is known to have issues and may not work well. It is <strong>strongly</strong> recommended that this file be saved using the modern PPTX format.';
} else if (guessed_extension == 'avi') {
warning_message = 'It appears this is a video file using the AVI format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG. The file will also likely be much smaller.';
} else if (guessed_extension == 'wmv') {
warning_message = 'It appears this is a video file using Microsoft\'s WMV format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG.';
} else {
warning_message = 'It appears this is a legacy or not very well supported file format. It is <strong>strongly</strong> recommended that it be saved in an alternative format if possible.';
}
} else if (file_size_bytes > 52428800) {
// 50 MB = 52428800 bytes
// 100 MB = 104857600 bytes
console.log(`This is a large file size ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes). Going to warn.`);
warning_size = true;
if (file_size_bytes > 2147483648) { // > 2 GB
warning_message = `This file size (${file_size_string}) is very large and will take at <strong>least</strong> a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Most audio, image, and video files can be compressed without a significant loss in quality. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly.`;
} else if (file_size_bytes > 209715200) { // > 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take at <strong>least</strong> a few minutes to upload depending on your network connection. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else if (file_size_bytes > 104857600) { // 100 MB to 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take a few minutes to upload depending on your network connection. Be sure you have a stable network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else { // 50 to 100 MB
warning_message = `This file size (${file_size_string}) is large and may take a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
}
} else {
}
file_data['warning_untrusted_extension'] = warning_untrusted_extension;
file_data['warning_legacy_extension'] = warning_legacy_extension;
file_data['warning_size'] = warning_size;
file_data['warning_message'] = warning_message;
file_data['uploaded'] = null;
file_data['uploaded_bytes'] = null;
// input_file_list_processed.push(JSON.parse(JSON.stringify(file_data)));
// input_file_list_processed = input_file_list_processed;
// console.log(get_file_hash(file_item));
// console.log(await get_file_hash(file_item));
let file_hash = null;
// Only hash files less than 2 GB (2147483648 bytes)!!!
console.log(`File size: ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes)`);
if (file_size_bytes < 2000000000) { // > 2 GB 2 147 483 648
file_hash = await ae_util.get_file_hash(file_item);
} else {
// File size in MB
console.log(`File is too large to hash. File size: ${file_size_bytes / 1048576} MB`);
}
if (file_hash) {
console.log(`Found file hash to lookup: ${ae_util.shorten_string({string: file_hash})}`);
file_data['hash_sha256'] = file_hash;
let check_hosted_file_obj_w_hash_result = await check_hosted_file_obj_w_hash({
api_cfg: $ae_api,
hosted_file_hash: file_hash
});
// console.log(check_hosted_file_obj_w_hash_result);
if (check_hosted_file_obj_w_hash_result && check_hosted_file_obj_w_hash_result.hosted_file_found_check) {
console.log('Matching hash!!!');
file_data['hash_sha256_match'] = true;
// $ae_events.pres_mgmt.new_upload_list[i].hash_sha256_match = true;
// $ae_events = $ae_events;
}
} else {
file_data['hash_sha256'] = null;
file_data['hash_sha256_match'] = false;
}
processed_file_list.push(file_data);
// input_file_list_processed.push(file_data);
}
file_list_status = 'ready';
console.log(processed_file_list);
// return JSON.parse(JSON.stringify(processed_file_list));
return processed_file_list
}
function remove_file_from_filelist(index) {
console.log('*** remove_file_from_filelist() ***');
// Can not use something like this because it is readonly:
const dt = new DataTransfer();
// let input = document.getElementById(input_element_id);
let input_element = document.querySelector('input[type="file"].svelte_input_file_element');
if (!input_element) {
console.error('Could not find the input element.');
return false;
}
let files = input_file_list;
if (!files || !files.length) {
console.error('No files found in the file list.');
file_list_status = null;
return false;
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (index !== i) // Only include the file if it does not match the index value.
dt.items.add(file);
}
// NOTE: I thought just setting the input_element.files OR input_file_list would trigger the input_file_list change.
// input_element.files = Object.assign({}, dt.files);
input_element.files = dt.files; // Assign the updates list
// input_file_list = null;
input_file_list = dt.files; // I feel like this should not need to be done, but doing it anyways.
return true;
}
import { createEventDispatcher, onMount, tick } from 'svelte';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
export let element_id = 'svelte_input_file_element';
export let input_name = 'file_list';
export let container_class_li: string[] = [];
export let input_class_li: string[] = ['file_drop_area'];
export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', '', 'text-sm'];
export let multiple: boolean = true;
export let required: boolean = true;
export let accept: string =
'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh';
export let untrusted_extension_list = ['bin', 'dmg', 'exe', 'js', 'msi', 'php', 'py', 'sh'];
export let legacy_extension_list = ['avi', 'doc', 'ppt', 'xls', 'wmv'];
export let use_selected_file_table = true;
export let file_list_status: null | string = null;
export let processed_file_list: any[] = [];
const dispatch = createEventDispatcher();
export let input_file_list: any = null;
let input_file_list_processed: any[] = [];
onMount(() => {
console.log('** Element Mounted: ** Element Input File');
});
$: if (input_file_list) {
console.log(input_file_list);
process_file_list(input_file_list).then(function (result) {
// console.log(result);
if (!result || !result.length) {
file_list_status = 'none';
}
// Save the results to the file upload list to be displayed as a table.
input_file_list_processed = result; // Includes file hash
dispatch('input_file_list_updated', {
element_id: element_id,
input_file_list: input_file_list,
input_file_list_processed: result // Includes file hash
});
});
}
async function process_file_list(file_list) {
console.log('*** process_file_list() ***');
file_list_status = 'processing';
processed_file_list = [];
if (!file_list) {
// file_list_processed = null;
file_list_status = 'none';
// await tick();
return processed_file_list;
}
// const forLoop = async _ => {
// console.log('*** Start ***');
// for (let [i, file_item] of file_list.entries()) { // Not sure why this does not work???
for await (const [i, file_item] of Array.prototype.entries.call(file_list)) {
console.log(i, file_item);
// NOTE: The file list is readonly. The filenames can not be changed here.
if (
file_item.name.endsWith('.odpmac') ||
file_item.name.endsWith('.odpwin') ||
file_item.name.endsWith('.pptmac') ||
file_item.name.endsWith('.pptwin') ||
file_item.name.endsWith('.pptxmac') ||
file_item.name.endsWith('.pptxwin')
) {
console.log('This file extension may need to be fixed? API upload will take care of it.');
// file_item.name = file_item.name.replace('.odpwin', '.odp');
}
let file_data: key_val = {};
let filename = file_item.name;
// console.log(filename);
file_data['filename'] = filename;
let guessed_extension = ae_util.guess_file_extension(filename);
file_data['guessed_extension'] = guessed_extension;
file_data['type'] = file_item.type;
let modified_date = new Date(file_item.lastModified);
file_data['modified_date'] = modified_date;
let modified_datetime_string = ae_util.iso_datetime_formatter(
modified_date,
'datetime_medium'
);
file_data['modified_datetime_string'] = modified_datetime_string;
let file_size_bytes = file_item.size;
file_data['file_size_bytes'] = file_size_bytes;
let file_size_string = ae_util.format_bytes(file_item.size, 2);
file_data['file_size_string'] = file_size_string;
// // NOTE: Calculate the hash of the file before upload. Check if this exact file has already been uploaded.
// let file_reader = new FileReader();
// file_reader.onload = async function() {
// const hash_buffer = crypto.subtle.digest('SHA-256', file_reader.result);
// let hash_hex_test = hash_buffer.then(async function (result_buffer) {
// const hash_array = Array.from(new Uint8Array(result_buffer)); // convert buffer to byte array
// const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// console.log(`File hash hex? ${hash_hex}`);
// file_data['hash_sha256'] = hash_hex;
// return hash_hex;
// })
// .catch(function (error: any) {
// console.log('Something went wrong?', error);
// });
// return hash_hex_test;
// // file_data['hash_hex_test'] = hash_hex_test;
// // const hash_buffer = await crypto.subtle.digest('SHA-256', file_reader.result);
// // let hash_str = await new TextDecoder().decode(hash_buffer);
// // console.log(`File hash string? ${hash_str}`);
// // file_data['hash_str'] = hash_str;
// // const hash_array = Array.from(new Uint8Array(hash_buffer)); // convert buffer to byte array
// // const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// // console.log(`File hash hex? ${hash_hex}`);
// // file_data['hash_sha256'] = hash_hex;
// // return hash_hex_test;
// // return hash_hex;
// }
// // file_reader.then(function (result) {
// // console.log(`File hash hex? ${result}`);
// // return hash_hex;
// // })
// // .catch(function (error: any) {
// // console.log('No results returned or failed.', error);
// // });
// // file_data['hash_sha256'] = file_reader.readAsArrayBuffer(file_item);
let warning_untrusted_extension = false;
let warning_legacy_extension = false;
let warning_size = false;
let warning_message = null;
if (untrusted_extension_list.includes(guessed_extension)) {
console.log('This is an untrusted extension. Going to warn.');
warning_untrusted_extension = true;
warning_message =
'It appears this an untrusted file type and is likely meant to be an executable or installable application. It is <strong>strongly</strong> recommended that this file not be used.';
} else if (legacy_extension_list.includes(guessed_extension)) {
console.log('This is a legacy extension. Going to warn.');
warning_legacy_extension = true;
if (guessed_extension == 'ppt') {
warning_message =
'It appears this is a legacy PowerPoint file and has not been officially supported since Office PowerPoint 2003. This file is known to have issues and may not work well. It is <strong>strongly</strong> recommended that this file be saved using the modern PPTX format.';
} else if (guessed_extension == 'avi') {
warning_message =
'It appears this is a video file using the AVI format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG. The file will also likely be much smaller.';
} else if (guessed_extension == 'wmv') {
warning_message =
"It appears this is a video file using Microsoft's WMV format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG.";
} else {
warning_message =
'It appears this is a legacy or not very well supported file format. It is <strong>strongly</strong> recommended that it be saved in an alternative format if possible.';
}
} else if (file_size_bytes > 52428800) {
// 50 MB = 52428800 bytes
// 100 MB = 104857600 bytes
console.log(
`This is a large file size ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes). Going to warn.`
);
warning_size = true;
if (file_size_bytes > 2147483648) {
// > 2 GB
warning_message = `This file size (${file_size_string}) is very large and will take at <strong>least</strong> a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Most audio, image, and video files can be compressed without a significant loss in quality. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly.`;
} else if (file_size_bytes > 209715200) {
// > 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take at <strong>least</strong> a few minutes to upload depending on your network connection. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else if (file_size_bytes > 104857600) {
// 100 MB to 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take a few minutes to upload depending on your network connection. Be sure you have a stable network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else {
// 50 to 100 MB
warning_message = `This file size (${file_size_string}) is large and may take a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
}
} else {
}
file_data['warning_untrusted_extension'] = warning_untrusted_extension;
file_data['warning_legacy_extension'] = warning_legacy_extension;
file_data['warning_size'] = warning_size;
file_data['warning_message'] = warning_message;
file_data['uploaded'] = null;
file_data['uploaded_bytes'] = null;
// input_file_list_processed.push(JSON.parse(JSON.stringify(file_data)));
// input_file_list_processed = input_file_list_processed;
// console.log(get_file_hash(file_item));
// console.log(await get_file_hash(file_item));
let file_hash = null;
// Only hash files less than 2 GB (2147483648 bytes)!!!
console.log(`File size: ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes)`);
if (file_size_bytes < 2000000000) {
// > 2 GB 2 147 483 648
file_hash = await ae_util.get_file_hash(file_item);
} else {
// File size in MB
console.log(`File is too large to hash. File size: ${file_size_bytes / 1048576} MB`);
}
if (file_hash) {
console.log(`Found file hash to lookup: ${ae_util.shorten_string({ string: file_hash })}`);
file_data['hash_sha256'] = file_hash;
let check_hosted_file_obj_w_hash_result = await check_hosted_file_obj_w_hash({
api_cfg: $ae_api,
hosted_file_hash: file_hash
});
// console.log(check_hosted_file_obj_w_hash_result);
if (
check_hosted_file_obj_w_hash_result &&
check_hosted_file_obj_w_hash_result.hosted_file_found_check
) {
console.log('Matching hash!!!');
file_data['hash_sha256_match'] = true;
// $ae_events.pres_mgmt.new_upload_list[i].hash_sha256_match = true;
// $ae_events = $ae_events;
}
} else {
file_data['hash_sha256'] = null;
file_data['hash_sha256_match'] = false;
}
processed_file_list.push(file_data);
// input_file_list_processed.push(file_data);
}
file_list_status = 'ready';
console.log(processed_file_list);
// return JSON.parse(JSON.stringify(processed_file_list));
return processed_file_list;
}
function remove_file_from_filelist(index) {
console.log('*** remove_file_from_filelist() ***');
// Can not use something like this because it is readonly:
const dt = new DataTransfer();
// let input = document.getElementById(input_element_id);
let input_element = document.querySelector('input[type="file"].svelte_input_file_element');
if (!input_element) {
console.error('Could not find the input element.');
return false;
}
let files = input_file_list;
if (!files || !files.length) {
console.error('No files found in the file list.');
file_list_status = null;
return false;
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (index !== i)
// Only include the file if it does not match the index value.
dt.items.add(file);
}
// NOTE: I thought just setting the input_element.files OR input_file_list would trigger the input_file_list change.
// input_element.files = Object.assign({}, dt.files);
input_element.files = dt.files; // Assign the updates list
// input_file_list = null;
input_file_list = dt.files; // I feel like this should not need to be done, but doing it anyways.
return true;
}
</script>
<div
class="svelte_element ae_element ae_input_file flex flex-col gap-1 items-center justify-center {container_class_li.join(
' '
)} text-center"
>
<label for={element_id} class="svelte_input_file_label text-center">
<div>
<span class="fas fa-upload"></span>
<!-- Select files to upload -->
<!-- <span class="fas fa-file-archive"></span> -->
<strong>Upload your files</strong>
<!-- (drag and drop) -->
</div>
<span>
Presentation related files only<br />
(PowerPoint, Keynote, PDF, mp4, Word Doc, Excel, txt, etc)
</span>
</label>
<div class="svelte_element ae_element ae_input_file flex flex-col gap-1 items-center justify-center {container_class_li.join(' ')} text-center">
<label
for={element_id}
class="svelte_input_file_label text-center"
>
<div>
<span class="fas fa-upload"></span>
<!-- Select files to upload -->
<!-- <span class="fas fa-file-archive"></span> -->
<strong>Upload your files</strong>
<!-- (drag and drop) -->
</div>
<span>
Presentation related files only<br>
(PowerPoint, Keynote, PDF, mp4, Word Doc, Excel, txt, etc)
</span>
</label>
<input
id={element_id}
type="file"
bind:files={input_file_list}
{multiple}
{required}
{accept}
name={input_name}
class="svelte_input_file_element {input_class_li.join(' ')}"
/>
<input
id={element_id}
type="file"
bind:files={input_file_list}
{multiple}
{required}
{accept}
name={input_name}
class="svelte_input_file_element {input_class_li.join(' ')}"
/>
{#if file_list_status == 'processing'}
<div class="file_list_status ae_warning preset-tonal-warning border border-warning-500 p-1 m-1">
<span class="fas fa-spinner fa-spin m-1"></span> Processing selected file list...
</div>
{/if}
{#if file_list_status == 'processing'}
<div class="file_list_status ae_warning preset-tonal-warning border border-warning-500 p-1 m-1">
<span class="fas fa-spinner fa-spin m-1"></span> Processing selected file list...
</div>
{/if}
<!-- {#await input_file_list_processed} -->
<!-- {:then} -->
{#if use_selected_file_table && input_file_list_processed && input_file_list_processed.length}
<strong>Files selected for upload</strong>
<table class="slct_file_list text-sm {table_class_li.join(' ')}">
<thead>
<tr>
<th>Remove</th>
<th>Filename</th>
<th>Modified</th>
<th>Size</th>
<!-- <th>Type</th> -->
<th>Ext</th>
<th>Hash</th>
</tr>
</thead>
{#each input_file_list_processed as file_list_item, file_index}
<tbody>
<tr>
<td class="file_remove">
<button
on:click|preventDefault={() => { (remove_file_from_filelist(file_index)); }}
class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
title="Remove file from upload list">
<span class="fas fa-minus"></span>
<span class="hidden">Remove</span>
</button>
</td>
<td class="file_filename">{file_list_item.filename}</td>
<td class="file_last_modified">{file_list_item.modified_datetime_string}</td>
<td
class="file_size"
class:bg-pink-200={file_list_item.warning_size}
>
{file_list_item.file_size_string}
<!-- {#if $ae_sess.api_upload_kv[link_to_id]}
<!-- {#await input_file_list_processed} -->
<!-- {:then} -->
{#if use_selected_file_table && input_file_list_processed && input_file_list_processed.length}
<strong>Files selected for upload</strong>
<table class="slct_file_list text-sm {table_class_li.join(' ')}">
<thead>
<tr>
<th>Remove</th>
<th>Filename</th>
<th>Modified</th>
<th>Size</th>
<!-- <th>Type</th> -->
<th>Ext</th>
<th>Hash</th>
</tr>
</thead>
{#each input_file_list_processed as file_list_item, file_index}
<tbody>
<tr>
<td class="file_remove">
<button
on:click|preventDefault={() => {
remove_file_from_filelist(file_index);
}}
class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
title="Remove file from upload list"
>
<span class="fas fa-minus"></span>
<span class="hidden">Remove</span>
</button>
</td>
<td class="file_filename">{file_list_item.filename}</td>
<td class="file_last_modified">{file_list_item.modified_datetime_string}</td>
<td class="file_size" class:bg-pink-200={file_list_item.warning_size}>
{file_list_item.file_size_string}
<!-- {#if $ae_sess.api_upload_kv[link_to_id]}
<span class="text-xs">({$ae_sess.api_upload_kv[link_to_id].percent_completed}%)</span>
{/if} -->
</td>
<!-- <td class="file_type" class:warning_file_untrusted_extension={file_list_item.warning_untrusted_extension} class:warning_file_legacy_extension={file_list_item.warning_legacy_extension}>{file_list_item.type}</td> -->
<td
class="file_extension"
class:bg-red-200={file_list_item.warning_untrusted_extension}
class:bg-pink-200={file_list_item.warning_legacy_extension}
>
{file_list_item.guessed_extension}
</td>
<td
class="file_hash file_hash256"
class:bg-pink-200={file_list_item.hash_sha256_match}
>
{ae_util.shorten_string({string: file_list_item.hash_sha256, begin_length: 5, end_length: 4, wildcard_length: 2})}
</td>
</tr>
</tbody>
{/each}
</table>
{/if}
</td>
<!-- <td class="file_type" class:warning_file_untrusted_extension={file_list_item.warning_untrusted_extension} class:warning_file_legacy_extension={file_list_item.warning_legacy_extension}>{file_list_item.type}</td> -->
<td
class="file_extension"
class:bg-red-200={file_list_item.warning_untrusted_extension}
class:bg-pink-200={file_list_item.warning_legacy_extension}
>
{file_list_item.guessed_extension}
</td>
<td class="file_hash file_hash256" class:bg-pink-200={file_list_item.hash_sha256_match}>
{ae_util.shorten_string({
string: file_list_item.hash_sha256,
begin_length: 5,
end_length: 4,
wildcard_length: 2
})}
</td>
</tr>
</tbody>
{/each}
</table>
{/if}
</div>
<style>
th {
text-align: center;
/* font-size: smaller; */
}
td {
text-align: center;
}
.file_last_modified {
font-size: smaller;
}
th {
text-align: center;
/* font-size: smaller; */
}
td {
text-align: center;
}
.file_last_modified {
font-size: smaller;
}
.file_size, .file_type {
/* font-size: smaller; */
}
.file_size,
.file_type {
/* font-size: smaller; */
}
.file_hash {
font-family: 'Courier New', Courier, monospace;
}
.file_hash {
font-family: 'Courier New', Courier, monospace;
}
</style>

View File

@@ -1,389 +1,406 @@
<script lang="ts">
import { createEventDispatcher, onMount, tick } from 'svelte';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
// export let element_id = 'svelte_input_file_element';
export let container_class_li: string[] = [];
export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', '' , 'text-sm'];
export let untrusted_extension_list = ['bin', 'dmg', 'exe', 'js', 'msi', 'php', 'py', 'sh'];
export let legacy_extension_list = ['avi', 'doc', 'ppt', 'xls', 'wmv'];
export let use_selected_file_table = true;
export let input_file_list: any = null;
export let file_list_status: null|string = null;
export let processed_file_list: any[] = [];
// const dispatch = createEventDispatcher();
// let input_file_list_processed: any[] = [];
onMount(() => {
console.log('** Element Mounted: ** Element Input File');
});
$: if (input_file_list) {
console.log(input_file_list);
process_file_list(input_file_list)
.then(function (result) {
// console.log(result);
if (!result || !result.length) {
processed_file_list = [];
file_list_status = 'none';
}
// Save the results to the file upload list to be displayed as a table.
// input_file_list_processed = result; // Includes file hash
// dispatch(
// 'input_file_list_updated',
// {
// element_id: element_id,
// input_file_list: input_file_list,
// input_file_list_processed: result, // Includes file hash
// }
// );
});
} else {
processed_file_list = [];
file_list_status = 'none';
}
async function process_file_list(file_list) {
console.log('*** process_file_list() ***');
file_list_status = 'processing';
processed_file_list = [];
if (!file_list) {
// file_list_processed = null;
file_list_status = 'none';
// await tick();
return processed_file_list;
}
// const forLoop = async _ => {
// console.log('*** Start ***');
// for (let [i, file_item] of file_list.entries()) { // Not sure why this does not work???
for await (const [i, file_item] of Array.prototype.entries.call(file_list)) {
console.log(i, file_item);
// NOTE: The file list is readonly. The filenames can not be changed here.
if (file_item.name.endsWith('.odpmac') || file_item.name.endsWith('.odpwin') || file_item.name.endsWith('.pptmac') || file_item.name.endsWith('.pptwin') || file_item.name.endsWith('.pptxmac') || file_item.name.endsWith('.pptxwin')) {
console.log('This file extension may need to be fixed? API upload will take care of it.');
// file_item.name = file_item.name.replace('.odpwin', '.odp');
}
let file_data: key_val = {};
let filename = file_item.name;
// console.log(filename);
file_data['filename'] = filename;
let guessed_extension = ae_util.guess_file_extension(filename);
file_data['guessed_extension'] = guessed_extension;
file_data['type'] = file_item.type;
let modified_date = new Date(file_item.lastModified);
file_data['modified_date'] = modified_date;
let modified_datetime_string = ae_util.iso_datetime_formatter(modified_date, 'datetime_medium');
file_data['modified_datetime_string'] = modified_datetime_string;
let file_size_bytes = file_item.size;
file_data['file_size_bytes'] = file_size_bytes;
let file_size_string = ae_util.format_bytes(file_item.size, 2);
file_data['file_size_string'] = file_size_string;
// // NOTE: Calculate the hash of the file before upload. Check if this exact file has already been uploaded.
// let file_reader = new FileReader();
// file_reader.onload = async function() {
// const hash_buffer = crypto.subtle.digest('SHA-256', file_reader.result);
// let hash_hex_test = hash_buffer.then(async function (result_buffer) {
// const hash_array = Array.from(new Uint8Array(result_buffer)); // convert buffer to byte array
// const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// console.log(`File hash hex? ${hash_hex}`);
// file_data['hash_sha256'] = hash_hex;
// return hash_hex;
// })
// .catch(function (error: any) {
// console.log('Something went wrong?', error);
// });
// return hash_hex_test;
// // file_data['hash_hex_test'] = hash_hex_test;
// // const hash_buffer = await crypto.subtle.digest('SHA-256', file_reader.result);
// // let hash_str = await new TextDecoder().decode(hash_buffer);
// // console.log(`File hash string? ${hash_str}`);
// // file_data['hash_str'] = hash_str;
// // const hash_array = Array.from(new Uint8Array(hash_buffer)); // convert buffer to byte array
// // const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// // console.log(`File hash hex? ${hash_hex}`);
// // file_data['hash_sha256'] = hash_hex;
// // return hash_hex_test;
// // return hash_hex;
// }
// // file_reader.then(function (result) {
// // console.log(`File hash hex? ${result}`);
// // return hash_hex;
// // })
// // .catch(function (error: any) {
// // console.log('No results returned or failed.', error);
// // });
// // file_data['hash_sha256'] = file_reader.readAsArrayBuffer(file_item);
let warning_untrusted_extension = false;
let warning_legacy_extension = false;
let warning_size = false;
let warning_message = null;
if (untrusted_extension_list.includes(guessed_extension)) {
console.log('This is an untrusted extension. Going to warn.')
warning_untrusted_extension = true;
warning_message = 'It appears this an untrusted file type and is likely meant to be an executable or installable application. It is <strong>strongly</strong> recommended that this file not be used.';
} else if (legacy_extension_list.includes(guessed_extension)) {
console.log('This is a legacy extension. Going to warn.')
warning_legacy_extension = true;
if (guessed_extension == 'ppt') {
warning_message = 'It appears this is a legacy PowerPoint file and has not been officially supported since Office PowerPoint 2003. This file is known to have issues and may not work well. It is <strong>strongly</strong> recommended that this file be saved using the modern PPTX format.';
} else if (guessed_extension == 'avi') {
warning_message = 'It appears this is a video file using the AVI format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG. The file will also likely be much smaller.';
} else if (guessed_extension == 'wmv') {
warning_message = 'It appears this is a video file using Microsoft\'s WMV format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG.';
} else {
warning_message = 'It appears this is a legacy or not very well supported file format. It is <strong>strongly</strong> recommended that it be saved in an alternative format if possible.';
}
} else if (file_size_bytes > 52428800) {
// 50 MB = 52428800 bytes
// 100 MB = 104857600 bytes
console.log(`This is a large file size ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes). Going to warn.`);
warning_size = true;
if (file_size_bytes > 2147483648) { // > 2 GB
warning_message = `This file size (${file_size_string}) is very large and will take at <strong>least</strong> a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Most audio, image, and video files can be compressed without a significant loss in quality. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly.`;
} else if (file_size_bytes > 209715200) { // > 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take at <strong>least</strong> a few minutes to upload depending on your network connection. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else if (file_size_bytes > 104857600) { // 100 MB to 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take a few minutes to upload depending on your network connection. Be sure you have a stable network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else { // 50 to 100 MB
warning_message = `This file size (${file_size_string}) is large and may take a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
}
} else {
}
file_data['warning_untrusted_extension'] = warning_untrusted_extension;
file_data['warning_legacy_extension'] = warning_legacy_extension;
file_data['warning_size'] = warning_size;
file_data['warning_message'] = warning_message;
file_data['uploaded'] = null;
file_data['uploaded_bytes'] = null;
// input_file_list_processed.push(JSON.parse(JSON.stringify(file_data)));
// input_file_list_processed = input_file_list_processed;
// console.log(get_file_hash(file_item));
// console.log(await get_file_hash(file_item));
let file_hash = null;
// Only hash files less than 2 GB (2147483648 bytes)!!!
console.log(`File size: ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes)`);
if (file_size_bytes < 2000000000) { // > 2 GB 2 147 483 648
file_hash = await ae_util.get_file_hash(file_item);
} else {
// File size in MB
console.log(`File is too large to hash. File size: ${file_size_bytes / 1048576} MB`);
}
if (file_hash) {
console.log(`Found file hash to lookup: ${ae_util.shorten_string({string: file_hash})}`);
file_data['hash_sha256'] = file_hash;
let check_hosted_file_obj_w_hash_result = await check_hosted_file_obj_w_hash({
api_cfg: $ae_api,
hosted_file_hash: file_hash
});
// console.log(check_hosted_file_obj_w_hash_result);
if (check_hosted_file_obj_w_hash_result && check_hosted_file_obj_w_hash_result.hosted_file_found_check) {
console.log('Matching hash!!!');
file_data['hash_sha256_match'] = true;
// $ae_events.pres_mgmt.new_upload_list[i].hash_sha256_match = true;
// $ae_events = $ae_events;
}
} else {
file_data['hash_sha256'] = null;
file_data['hash_sha256_match'] = false;
}
processed_file_list.push(file_data);
// input_file_list_processed.push(file_data);
}
file_list_status = 'ready';
console.log(processed_file_list);
// return JSON.parse(JSON.stringify(processed_file_list));
return processed_file_list
}
function remove_file_from_filelist(index) {
console.log('*** remove_file_from_filelist() ***');
// Can not use something like this because it is readonly:
const dt = new DataTransfer();
// let input = document.getElementById(input_element_id);
let input_element = document.querySelector('input[type="file"].svelte_input_file_element');
if (!input_element) {
console.error('Could not find the input element.');
return false;
}
let files = input_file_list;
if (!files || !files.length) {
console.error('No files found in the file list.');
file_list_status = null;
return false;
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (index !== i) // Only include the file if it does not match the index value.
dt.items.add(file);
}
// NOTE: I thought just setting the input_element.files OR input_file_list would trigger the input_file_list change.
// input_element.files = Object.assign({}, dt.files);
input_element.files = dt.files; // Assign the updates list
// input_file_list = null;
input_file_list = dt.files; // I feel like this should not need to be done, but doing it anyways.
return true;
}
import { createEventDispatcher, onMount, tick } from 'svelte';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
// export let element_id = 'svelte_input_file_element';
export let container_class_li: string[] = [];
export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', '', 'text-sm'];
export let untrusted_extension_list = ['bin', 'dmg', 'exe', 'js', 'msi', 'php', 'py', 'sh'];
export let legacy_extension_list = ['avi', 'doc', 'ppt', 'xls', 'wmv'];
export let use_selected_file_table = true;
export let input_file_list: any = null;
export let file_list_status: null | string = null;
export let processed_file_list: any[] = [];
// const dispatch = createEventDispatcher();
// let input_file_list_processed: any[] = [];
onMount(() => {
console.log('** Element Mounted: ** Element Input File');
});
$: if (input_file_list) {
console.log(input_file_list);
process_file_list(input_file_list).then(function (result) {
// console.log(result);
if (!result || !result.length) {
processed_file_list = [];
file_list_status = 'none';
}
// Save the results to the file upload list to be displayed as a table.
// input_file_list_processed = result; // Includes file hash
// dispatch(
// 'input_file_list_updated',
// {
// element_id: element_id,
// input_file_list: input_file_list,
// input_file_list_processed: result, // Includes file hash
// }
// );
});
} else {
processed_file_list = [];
file_list_status = 'none';
}
async function process_file_list(file_list) {
console.log('*** process_file_list() ***');
file_list_status = 'processing';
processed_file_list = [];
if (!file_list) {
// file_list_processed = null;
file_list_status = 'none';
// await tick();
return processed_file_list;
}
// const forLoop = async _ => {
// console.log('*** Start ***');
// for (let [i, file_item] of file_list.entries()) { // Not sure why this does not work???
for await (const [i, file_item] of Array.prototype.entries.call(file_list)) {
console.log(i, file_item);
// NOTE: The file list is readonly. The filenames can not be changed here.
if (
file_item.name.endsWith('.odpmac') ||
file_item.name.endsWith('.odpwin') ||
file_item.name.endsWith('.pptmac') ||
file_item.name.endsWith('.pptwin') ||
file_item.name.endsWith('.pptxmac') ||
file_item.name.endsWith('.pptxwin')
) {
console.log('This file extension may need to be fixed? API upload will take care of it.');
// file_item.name = file_item.name.replace('.odpwin', '.odp');
}
let file_data: key_val = {};
let filename = file_item.name;
// console.log(filename);
file_data['filename'] = filename;
let guessed_extension = ae_util.guess_file_extension(filename);
file_data['guessed_extension'] = guessed_extension;
file_data['type'] = file_item.type;
let modified_date = new Date(file_item.lastModified);
file_data['modified_date'] = modified_date;
let modified_datetime_string = ae_util.iso_datetime_formatter(
modified_date,
'datetime_medium'
);
file_data['modified_datetime_string'] = modified_datetime_string;
let file_size_bytes = file_item.size;
file_data['file_size_bytes'] = file_size_bytes;
let file_size_string = ae_util.format_bytes(file_item.size, 2);
file_data['file_size_string'] = file_size_string;
// // NOTE: Calculate the hash of the file before upload. Check if this exact file has already been uploaded.
// let file_reader = new FileReader();
// file_reader.onload = async function() {
// const hash_buffer = crypto.subtle.digest('SHA-256', file_reader.result);
// let hash_hex_test = hash_buffer.then(async function (result_buffer) {
// const hash_array = Array.from(new Uint8Array(result_buffer)); // convert buffer to byte array
// const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// console.log(`File hash hex? ${hash_hex}`);
// file_data['hash_sha256'] = hash_hex;
// return hash_hex;
// })
// .catch(function (error: any) {
// console.log('Something went wrong?', error);
// });
// return hash_hex_test;
// // file_data['hash_hex_test'] = hash_hex_test;
// // const hash_buffer = await crypto.subtle.digest('SHA-256', file_reader.result);
// // let hash_str = await new TextDecoder().decode(hash_buffer);
// // console.log(`File hash string? ${hash_str}`);
// // file_data['hash_str'] = hash_str;
// // const hash_array = Array.from(new Uint8Array(hash_buffer)); // convert buffer to byte array
// // const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
// // console.log(`File hash hex? ${hash_hex}`);
// // file_data['hash_sha256'] = hash_hex;
// // return hash_hex_test;
// // return hash_hex;
// }
// // file_reader.then(function (result) {
// // console.log(`File hash hex? ${result}`);
// // return hash_hex;
// // })
// // .catch(function (error: any) {
// // console.log('No results returned or failed.', error);
// // });
// // file_data['hash_sha256'] = file_reader.readAsArrayBuffer(file_item);
let warning_untrusted_extension = false;
let warning_legacy_extension = false;
let warning_size = false;
let warning_message = null;
if (untrusted_extension_list.includes(guessed_extension)) {
console.log('This is an untrusted extension. Going to warn.');
warning_untrusted_extension = true;
warning_message =
'It appears this an untrusted file type and is likely meant to be an executable or installable application. It is <strong>strongly</strong> recommended that this file not be used.';
} else if (legacy_extension_list.includes(guessed_extension)) {
console.log('This is a legacy extension. Going to warn.');
warning_legacy_extension = true;
if (guessed_extension == 'ppt') {
warning_message =
'It appears this is a legacy PowerPoint file and has not been officially supported since Office PowerPoint 2003. This file is known to have issues and may not work well. It is <strong>strongly</strong> recommended that this file be saved using the modern PPTX format.';
} else if (guessed_extension == 'avi') {
warning_message =
'It appears this is a video file using the AVI format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG. The file will also likely be much smaller.';
} else if (guessed_extension == 'wmv') {
warning_message =
"It appears this is a video file using Microsoft's WMV format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG.";
} else {
warning_message =
'It appears this is a legacy or not very well supported file format. It is <strong>strongly</strong> recommended that it be saved in an alternative format if possible.';
}
} else if (file_size_bytes > 52428800) {
// 50 MB = 52428800 bytes
// 100 MB = 104857600 bytes
console.log(
`This is a large file size ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes). Going to warn.`
);
warning_size = true;
if (file_size_bytes > 2147483648) {
// > 2 GB
warning_message = `This file size (${file_size_string}) is very large and will take at <strong>least</strong> a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Most audio, image, and video files can be compressed without a significant loss in quality. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly.`;
} else if (file_size_bytes > 209715200) {
// > 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take at <strong>least</strong> a few minutes to upload depending on your network connection. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else if (file_size_bytes > 104857600) {
// 100 MB to 200 MB
warning_message = `This file size (${file_size_string}) is large and will likely take a few minutes to upload depending on your network connection. Be sure you have a stable network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
} else {
// 50 to 100 MB
warning_message = `This file size (${file_size_string}) is large and may take a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
}
} else {
}
file_data['warning_untrusted_extension'] = warning_untrusted_extension;
file_data['warning_legacy_extension'] = warning_legacy_extension;
file_data['warning_size'] = warning_size;
file_data['warning_message'] = warning_message;
file_data['uploaded'] = null;
file_data['uploaded_bytes'] = null;
// input_file_list_processed.push(JSON.parse(JSON.stringify(file_data)));
// input_file_list_processed = input_file_list_processed;
// console.log(get_file_hash(file_item));
// console.log(await get_file_hash(file_item));
let file_hash = null;
// Only hash files less than 2 GB (2147483648 bytes)!!!
console.log(`File size: ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes)`);
if (file_size_bytes < 2000000000) {
// > 2 GB 2 147 483 648
file_hash = await ae_util.get_file_hash(file_item);
} else {
// File size in MB
console.log(`File is too large to hash. File size: ${file_size_bytes / 1048576} MB`);
}
if (file_hash) {
console.log(`Found file hash to lookup: ${ae_util.shorten_string({ string: file_hash })}`);
file_data['hash_sha256'] = file_hash;
let check_hosted_file_obj_w_hash_result = await check_hosted_file_obj_w_hash({
api_cfg: $ae_api,
hosted_file_hash: file_hash
});
// console.log(check_hosted_file_obj_w_hash_result);
if (
check_hosted_file_obj_w_hash_result &&
check_hosted_file_obj_w_hash_result.hosted_file_found_check
) {
console.log('Matching hash!!!');
file_data['hash_sha256_match'] = true;
// $ae_events.pres_mgmt.new_upload_list[i].hash_sha256_match = true;
// $ae_events = $ae_events;
}
} else {
file_data['hash_sha256'] = null;
file_data['hash_sha256_match'] = false;
}
processed_file_list.push(file_data);
// input_file_list_processed.push(file_data);
}
file_list_status = 'ready';
console.log(processed_file_list);
// return JSON.parse(JSON.stringify(processed_file_list));
return processed_file_list;
}
function remove_file_from_filelist(index) {
console.log('*** remove_file_from_filelist() ***');
// Can not use something like this because it is readonly:
const dt = new DataTransfer();
// let input = document.getElementById(input_element_id);
let input_element = document.querySelector('input[type="file"].svelte_input_file_element');
if (!input_element) {
console.error('Could not find the input element.');
return false;
}
let files = input_file_list;
if (!files || !files.length) {
console.error('No files found in the file list.');
file_list_status = null;
return false;
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (index !== i)
// Only include the file if it does not match the index value.
dt.items.add(file);
}
// NOTE: I thought just setting the input_element.files OR input_file_list would trigger the input_file_list change.
// input_element.files = Object.assign({}, dt.files);
input_element.files = dt.files; // Assign the updates list
// input_file_list = null;
input_file_list = dt.files; // I feel like this should not need to be done, but doing it anyways.
return true;
}
</script>
<div
class="svelte_element ae_element ae_input_file flex flex-col gap-1 items-center justify-center {container_class_li.join(
' '
)} text-center"
>
{#if file_list_status == 'processing'}
<div class="file_list_status ae_warning preset-tonal-warning border border-warning-500 p-1 m-1">
<span class="fas fa-spinner fa-spin m-1"></span> Processing selected file list...
</div>
{/if}
<div class="svelte_element ae_element ae_input_file flex flex-col gap-1 items-center justify-center {container_class_li.join(' ')} text-center">
{#if file_list_status == 'processing'}
<div class="file_list_status ae_warning preset-tonal-warning border border-warning-500 p-1 m-1">
<span class="fas fa-spinner fa-spin m-1"></span> Processing selected file list...
</div>
{/if}
<!-- {#await processed_file_list} -->
<!-- {:then} -->
{#if use_selected_file_table && processed_file_list && processed_file_list.length}
<strong>Files selected for upload</strong>
<table class="slct_file_list text-sm {table_class_li.join(' ')}">
<thead>
<tr>
<th>Remove</th>
<th>Filename</th>
<th>Modified</th>
<th>Size</th>
<!-- <th>Type</th> -->
<th>Ext</th>
<th>Hash</th>
</tr>
</thead>
{#each processed_file_list as file_list_item, file_index}
<tbody>
<tr>
<td class="file_remove">
<button
on:click|preventDefault={() => { (remove_file_from_filelist(file_index)); }}
class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
title="Remove file from upload list">
<span class="fas fa-minus"></span>
<span class="hidden">Remove</span>
</button>
</td>
<td class="file_filename text-wrap break-all md:break-words">
{file_list_item.filename}
</td>
<td class="file_last_modified">{file_list_item.modified_datetime_string}</td>
<td
class="file_size"
class:bg-pink-200={file_list_item.warning_size}
>
{file_list_item.file_size_string}
{#if $ae_sess.api_upload_kv[file_list_item.hash_sha256]}
<span class="text-xs">({$ae_sess.api_upload_kv[file_list_item.hash_sha256].percent_completed}%)</span>
{/if}
</td>
<!-- <td class="file_type" class:warning_file_untrusted_extension={file_list_item.warning_untrusted_extension} class:warning_file_legacy_extension={file_list_item.warning_legacy_extension}>{file_list_item.type}</td> -->
<td
class="file_extension"
class:bg-red-200={file_list_item.warning_untrusted_extension}
class:bg-pink-200={file_list_item.warning_legacy_extension}
>
{file_list_item.guessed_extension}
</td>
<td
class="file_hash file_hash256"
class:bg-pink-200={file_list_item.hash_sha256_match}
>
{ae_util.shorten_string({string: file_list_item.hash_sha256, begin_length: 5, end_length: 4, wildcard_length: 2})}
</td>
</tr>
</tbody>
{/each}
</table>
{/if}
<!-- {#await processed_file_list} -->
<!-- {:then} -->
{#if use_selected_file_table && processed_file_list && processed_file_list.length}
<strong>Files selected for upload</strong>
<table class="slct_file_list text-sm {table_class_li.join(' ')}">
<thead>
<tr>
<th>Remove</th>
<th>Filename</th>
<th>Modified</th>
<th>Size</th>
<!-- <th>Type</th> -->
<th>Ext</th>
<th>Hash</th>
</tr>
</thead>
{#each processed_file_list as file_list_item, file_index}
<tbody>
<tr>
<td class="file_remove">
<button
on:click|preventDefault={() => {
remove_file_from_filelist(file_index);
}}
class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
title="Remove file from upload list"
>
<span class="fas fa-minus"></span>
<span class="hidden">Remove</span>
</button>
</td>
<td class="file_filename text-wrap break-all md:break-words">
{file_list_item.filename}
</td>
<td class="file_last_modified">{file_list_item.modified_datetime_string}</td>
<td class="file_size" class:bg-pink-200={file_list_item.warning_size}>
{file_list_item.file_size_string}
{#if $ae_sess.api_upload_kv[file_list_item.hash_sha256]}
<span class="text-xs"
>({$ae_sess.api_upload_kv[file_list_item.hash_sha256].percent_completed}%)</span
>
{/if}
</td>
<!-- <td class="file_type" class:warning_file_untrusted_extension={file_list_item.warning_untrusted_extension} class:warning_file_legacy_extension={file_list_item.warning_legacy_extension}>{file_list_item.type}</td> -->
<td
class="file_extension"
class:bg-red-200={file_list_item.warning_untrusted_extension}
class:bg-pink-200={file_list_item.warning_legacy_extension}
>
{file_list_item.guessed_extension}
</td>
<td class="file_hash file_hash256" class:bg-pink-200={file_list_item.hash_sha256_match}>
{ae_util.shorten_string({
string: file_list_item.hash_sha256,
begin_length: 5,
end_length: 4,
wildcard_length: 2
})}
</td>
</tr>
</tbody>
{/each}
</table>
{/if}
</div>
<style>
th {
text-align: center;
/* font-size: smaller; */
}
td {
text-align: center;
}
.file_last_modified {
font-size: smaller;
}
th {
text-align: center;
/* font-size: smaller; */
}
td {
text-align: center;
}
.file_last_modified {
font-size: smaller;
}
.file_size, .file_type {
/* font-size: smaller; */
}
.file_size,
.file_type {
/* font-size: smaller; */
}
.file_hash {
font-family: 'Courier New', Courier, monospace;
}
.file_hash {
font-family: 'Courier New', Courier, monospace;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +1,70 @@
<script lang="ts">
interface Props {
// import { events_func } from '$lib/ae_events_functions';
container_class_li?: string|Array<string>;
link_to_type: string;
link_to_id: string;
allow_basic?: boolean;
allow_moderator?: boolean;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
}
interface Props {
// import { events_func } from '$lib/ae_events_functions';
container_class_li?: string | Array<string>;
link_to_type: string;
link_to_id: string;
allow_basic?: boolean;
allow_moderator?: boolean;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
}
let {
container_class_li = [],
link_to_type,
link_to_id,
allow_basic = false,
allow_moderator = false,
display_mode = 'default'
}: Props = $props();
let {
container_class_li = [],
link_to_type,
link_to_id,
allow_basic = false,
allow_moderator = false,
display_mode = 'default'
}: Props = $props();
import { liveQuery } from "dexie";
import { liveQuery } from 'dexie';
import type { key_val } from '$lib/stores/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import Element_manage_event_file_li from '$lib/elements/element_manage_event_file_li.svelte';
import type { key_val } from '$lib/stores/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import Element_manage_event_file_li from '$lib/elements/element_manage_event_file_li.svelte';
// import { core_func } from '$lib/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_events } from "$lib/ae_events/db_events";
// import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
// import { core_func } from '$lib/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_events } from '$lib/ae_events/db_events';
// import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
// export let show_convert_btn: null|boolean = null;
// export let show_convert_btn: null|boolean = null;
// let ae_placeholder_li: key_val = {};
// let ae_promises: key_val = {};
let ae_tmp: key_val = {};
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = false;
// let ae_triggers: key_val = {};
// let ae_placeholder_li: key_val = {};
// let ae_promises: key_val = {};
let ae_tmp: key_val = {};
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = false;
// let ae_triggers: key_val = {};
let dq__where_val: string = `${link_to_type}_id_random`;
let dq__where_eq_val: string = link_to_id;
let dq__where_val: string = `${link_to_type}_id_random`;
let dq__where_eq_val: string = link_to_id;
// This should include all files that are associated with an object (event, location, session, presenter, etc.)
// I am not sure why, but doing reverse() and then sortBy() seems to sort in descending order.
let lq__event_file_obj_li = $derived(liveQuery(async () => {
let results = await db_events.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.reverse()
.sortBy('created_on')
// .toArray()
;
return results;
}));
// This should include all files that are associated with an object (event, location, session, presenter, etc.)
// I am not sure why, but doing reverse() and then sortBy() seems to sort in descending order.
let lq__event_file_obj_li = $derived(
liveQuery(async () => {
let results = await db_events.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.reverse()
.sortBy('created_on');
// .toArray()
return results;
})
);
</script>
<Element_manage_event_file_li
link_to_type={link_to_type}
link_to_id={link_to_id}
lq__event_file_obj_li={lq__event_file_obj_li}
allow_basic={allow_basic}
allow_moderator={allow_moderator}
container_class_li={container_class_li}
display_mode={display_mode}
/>
{link_to_type}
{link_to_id}
{lq__event_file_obj_li}
{allow_basic}
{allow_moderator}
{container_class_li}
{display_mode}
/>

View File

@@ -1,82 +1,80 @@
<script lang="ts">
interface Props {
log_lvl?: number;
// import { events_func } from '$lib/ae_events_functions';
container_class_li?: string|Array<string>;
link_to_type: string;
link_to_id: string;
allow_basic?: boolean;
allow_moderator?: boolean;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
}
interface Props {
log_lvl?: number;
// import { events_func } from '$lib/ae_events_functions';
container_class_li?: string | Array<string>;
link_to_type: string;
link_to_id: string;
allow_basic?: boolean;
allow_moderator?: boolean;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
}
let {
log_lvl = 0,
container_class_li = [],
link_to_type,
link_to_id,
allow_basic = false,
allow_moderator = false,
display_mode = 'default'
}: Props = $props();
let {
log_lvl = 0,
container_class_li = [],
link_to_type,
link_to_id,
allow_basic = false,
allow_moderator = false,
display_mode = 'default'
}: Props = $props();
import { liveQuery } from "dexie";
import { liveQuery } from 'dexie';
import type { key_val } from '$lib/stores/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import Element_manage_event_file_li from '$lib/elements/element_manage_event_file_li.svelte';
import type { key_val } from '$lib/stores/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import Element_manage_event_file_li from '$lib/elements/element_manage_event_file_li.svelte';
// import { core_func } from '$lib/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_events } from "$lib/ae_events/db_events";
// import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
// import { core_func } from '$lib/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_events } from '$lib/ae_events/db_events';
// import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
// export let show_convert_btn: null|boolean = null;
// export let show_convert_btn: null|boolean = null;
// let ae_promises: key_val = {};
let ae_tmp: key_val = {};
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = false;
// let ae_triggers: key_val = {};
// let ae_promises: key_val = {};
let ae_tmp: key_val = {};
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = false;
// let ae_triggers: key_val = {};
let dq__where_val: string = `for_type`;
let dq__where_eq_val: string = link_to_type;
let dq__where_for_id_eq_val: string = link_to_id;
let dq__where_val: string = `for_type`;
let dq__where_eq_val: string = link_to_type;
let dq__where_for_id_eq_val: string = link_to_id;
// This should only include files that are directly linked to an object (event, location, session, presenter, etc.).
// I am not sure why, but doing reverse() and then sortBy() seems to sort in descending order.
let lq__event_file_obj_li = $derived(liveQuery(async () => {
let results = await db_events.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and(file => file.for_id == dq__where_for_id_eq_val)
.reverse()
.sortBy('created_on')
// .toArray()
;
return results;
}));
// This should only include files that are directly linked to an object (event, location, session, presenter, etc.).
// I am not sure why, but doing reverse() and then sortBy() seems to sort in descending order.
let lq__event_file_obj_li = $derived(
liveQuery(async () => {
let results = await db_events.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((file) => file.for_id == dq__where_for_id_eq_val)
.reverse()
.sortBy('created_on');
// .toArray()
return results;
})
);
</script>
{#await lq__event_file_obj_li}
<p>Loading...</p>
<p>Loading...</p>
{:then lq__event_file_obj_li}
<Element_manage_event_file_li
link_to_type={link_to_type}
link_to_id={link_to_id}
lq__event_file_obj_li={lq__event_file_obj_li}
allow_basic={allow_basic}
allow_moderator={allow_moderator}
container_class_li={container_class_li}
display_mode={display_mode}
log_lvl={log_lvl}
/>
<Element_manage_event_file_li
{link_to_type}
{link_to_id}
{lq__event_file_obj_li}
{allow_basic}
{allow_moderator}
{container_class_li}
{display_mode}
{log_lvl}
/>
{:catch error}
<p style="color: red;">{error.message}</p>
<p style="color: red;">{error.message}</p>
{/await}

View File

@@ -1,269 +1,245 @@
<script lang="ts">
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
// import { core_func } from '$lib/ae_core_functions';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
import { db_core } from "$lib/ae_core/db_core";
import { core_func } from '$lib/ae_core/ae_core_functions';
// import { core_func } from '$lib/ae_core_functions';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
import { db_core } from '$lib/ae_core/db_core';
import { core_func } from '$lib/ae_core/ae_core_functions';
// export let allow_basic: boolean = false;
// export let allow_basic: boolean = false;
interface Props {
log_lvl?: number;
class_li_default?: string;
class_li?: string;
lq__hosted_file_obj_li: any;
link_to_type: string;
link_to_id: string;
// export let allow_moderator: boolean = false;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
max_file_count?: number;
file_type?: string; // 'image', 'video', 'audio', 'document', 'other'
slct_hosted_file_kv?: key_val;
slct_hosted_file_id?: any;
slct_hosted_file_obj?: any;
}
interface Props {
log_lvl?: number;
class_li_default?: string;
class_li?: string;
lq__hosted_file_obj_li: any;
link_to_type: string;
link_to_id: string;
// export let allow_moderator: boolean = false;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
max_file_count?: number;
file_type?: string; // 'image', 'video', 'audio', 'document', 'other'
slct_hosted_file_kv?: key_val;
slct_hosted_file_id?: any;
slct_hosted_file_obj?: any;
}
let {
log_lvl = 0,
class_li_default = 'flex flex-col gap-1 items-center justify-center w-fit max-w-4xl mx-auto my-1 max-h-96 overflow-auto',
class_li = '',
lq__hosted_file_obj_li = $bindable([]),
link_to_type,
link_to_id,
display_mode = 'default',
max_file_count = 49,
file_type = 'all',
slct_hosted_file_kv = $bindable({}),
slct_hosted_file_id = $bindable(null),
slct_hosted_file_obj = $bindable(null)
}: Props = $props();
let {
log_lvl = 0,
class_li_default = 'flex flex-col gap-1 items-center justify-center w-fit max-w-4xl mx-auto my-1 max-h-96 overflow-auto',
class_li = '',
lq__hosted_file_obj_li = $bindable([]),
link_to_type,
link_to_id,
display_mode = 'default',
max_file_count = 49,
file_type = 'all',
slct_hosted_file_kv = $bindable({}),
slct_hosted_file_id = $bindable(null),
slct_hosted_file_obj = $bindable(null)
}: Props = $props();
// export let show_convert_btn: null|boolean = null;
// let ae_placeholder_li: key_val = {};
let ae_promises: key_val = {};
let ae_tmp: key_val = $state({});
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = $ae_loc.core?.show__direct_download ?? false;
// let ae_triggers: key_val = {};
// export let show_convert_btn: null|boolean = null;
// let ae_placeholder_li: key_val = {};
let ae_promises: key_val = {};
let ae_tmp: key_val = $state({});
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = $ae_loc.core?.show__direct_download ?? false;
// let ae_triggers: key_val = {};
</script>
<section class="{class_li_default} {class_li}">
<h3 class="h3" class:hidden={!$lq__hosted_file_obj_li?.length || display_mode != 'default'}>
Manage Files:
<span
class="font-bold bg-success-100 px-4 border rounded-lg border-success-200"
title="Files for {link_to_type ?? '-- not set --'}: {link_to_id ??
'-- not set --'} (files: {$lq__hosted_file_obj_li?.length ?? 'None'})"
>
<span class="fas fa-folder-open mx-1"></span>
{@html $lq__hosted_file_obj_li ? `${$lq__hosted_file_obj_li.length}&times;` : '-- none --'}
</span>
</h3>
<section
class="{class_li_default} {class_li}"
>
<div class="flex flex-row flex-wrap items-center justify-center gap-1">
<button
type="button"
onclick={() => {
console.log('*** Refresh button clicked ***');
<h3
class="h3"
class:hidden={!$lq__hosted_file_obj_li?.length || display_mode != 'default'}
>
Manage Files:
<span class="font-bold bg-success-100 px-4 border rounded-lg border-success-200"
title="Files for {link_to_type ?? '-- not set --'}: {link_to_id ?? '-- not set --'} (files: {$lq__hosted_file_obj_li?.length ?? 'None'})"
>
<span class="fas fa-folder-open mx-1"></span>
{@html $lq__hosted_file_obj_li ? `${$lq__hosted_file_obj_li.length}&times;` : '-- none --'}
</span>
</h3>
db_core.file.clear();
<div
class="flex flex-row flex-wrap items-center justify-center gap-1"
>
<button
type="button"
onclick={() => {
console.log('*** Refresh button clicked ***');
// let params = {
// qry__enabled: 'all',
// qry__hidden: 'all',
// }
db_core.file.clear();
core_func.load_ae_obj_li__hosted_file({
api_cfg: $ae_api,
for_obj_type: link_to_type,
for_obj_id: link_to_id,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 250,
// params: params,
try_cache: true,
log_lvl: 2
});
// let params = {
// qry__enabled: 'all',
// qry__hidden: 'all',
// }
// ae_tmp.show__file_li = false;
// console.log(`$lq__hosted_file_obj_li:`, $lq__hosted_file_obj_li);
// $slct_trigger = 'load__hosted_file_obj_li';
// ae_tmp.show__file_li = true;
}}
class="btn btn-sm p-1 m-1 preset-tonal-tertiary hover:preset-tonal-warning border border-warning-500 transition hover:transition-all *:hover:inline"
class:hidden={!$ae_loc.edit_mode || !$ae_loc.authenticated_access}
title="Refresh the list of files"
>
<span class="fas fa-sync-alt m-1"></span>
<div class="hidden">Files</div>
</button>
core_func.load_ae_obj_li__hosted_file({
api_cfg: $ae_api,
for_obj_type: link_to_type,
for_obj_id: link_to_id,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 250,
// params: params,
try_cache: true,
log_lvl: 2
});
<button
type="button"
onclick={() => {
console.log('*** Show Alt Download button clicked ***');
ae_tmp.show__direct_download = !ae_tmp.show__direct_download;
}}
class="btn btn-sm p-1 m-1 preset-tonal-tertiary hover:preset-tonal-warning border border-warning-500 transition hover:transition-all *:hover:inline"
class:hidden={!$ae_loc.edit_mode || !$ae_loc.trusted_access}
title="Toggle direct download link and copy link button"
>
<span class="fas fa-download m-1"></span>
<div class="hidden">
{ae_tmp.show__direct_download ? 'Alt On' : 'Alt Download Off'}
</div>
</button>
</div>
// ae_tmp.show__file_li = false;
// console.log(`$lq__hosted_file_obj_li:`, $lq__hosted_file_obj_li);
// $slct_trigger = 'load__hosted_file_obj_li';
// ae_tmp.show__file_li = true;
}}
class="btn btn-sm p-1 m-1 preset-tonal-tertiary hover:preset-tonal-warning border border-warning-500 transition hover:transition-all *:hover:inline"
class:hidden={!$ae_loc.edit_mode || !$ae_loc.authenticated_access}
title="Refresh the list of files"
>
<span class="fas fa-sync-alt m-1"></span>
<div class="hidden">
Files
</div>
</button>
{#if $lq__hosted_file_obj_li && $lq__hosted_file_obj_li.length}
<div class="overflow-auto w-full">
<ol class="list-decimal list-inside">
{#each [...$lq__hosted_file_obj_li].reverse().slice(0, max_file_count) as hosted_file_obj}
<li>
<button
type="button"
disabled={!$ae_loc.trusted_access}
onclick={() => {
// This (uploaded_file_kv) is referenced by other AE components. Currently it is only used for the video clipper. This should be a toggle of Add/Remove.
if ($ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id]) {
delete $ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id];
$ae_loc.files.uploaded_file_kv = { ...$ae_loc.files.uploaded_file_kv };
<button
type="button"
onclick={() => {
console.log('*** Show Alt Download button clicked ***');
ae_tmp.show__direct_download = !ae_tmp.show__direct_download;
}}
class="btn btn-sm p-1 m-1 preset-tonal-tertiary hover:preset-tonal-warning border border-warning-500 transition hover:transition-all *:hover:inline"
class:hidden={!$ae_loc.edit_mode || !$ae_loc.trusted_access}
title="Toggle direct download link and copy link button"
>
<span class="fas fa-download m-1"></span>
<div class="hidden">
{ae_tmp.show__direct_download ? 'Alt On' : 'Alt Download Off'}
</div>
</button>
</div>
delete slct_hosted_file_kv[hosted_file_obj.hosted_file_id];
slct_hosted_file_id = null;
slct_hosted_file_obj = null;
} else {
$ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id] = hosted_file_obj;
lq__hosted_file_obj_li[hosted_file_obj.hosted_file_id] = hosted_file_obj;
slct_hosted_file_kv[hosted_file_obj.hosted_file_id] = hosted_file_obj;
slct_hosted_file_id = hosted_file_obj.hosted_file_id;
slct_hosted_file_obj = hosted_file_obj;
}
log_lvl = 1;
if (log_lvl) {
console.log(`slct_hosted_file_kv:`, slct_hosted_file_kv);
}
}}
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500"
title="Add/Remove file to/from the locally stored uploaded file list. This is referenced by other AE components."
>
{#if $ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id]}
<span class="fas fa-minus-circle m-1"></span>
<span class="hidden">Remove</span>
{:else}
<span class="fas fa-plus-circle m-1"></span>
<span class="hidden">Add</span>
{/if}
</button>
<button
type="button"
disabled={!$ae_loc.administrator_access}
onclick={() => {
if (
!confirm(
`Are you sure you want to delete this hosted file?\n${hosted_file_obj.filename} [${hosted_file_obj.hosted_file_id_random}]`
)
) {
return false;
}
ae_promises.delete__hosted_file_obj = core_func.delete_ae_obj_id__hosted_file({
api_cfg: $ae_api,
hosted_file_id: hosted_file_obj.hosted_file_id_random,
link_to_type: link_to_type,
link_to_id: link_to_id,
rm_orphan: true,
fake_delete: false,
log_lvl: 1
});
}}
class:hidden={!$ae_loc.administrator_access}
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500"
title="Delete a file from the host server."
>
<span class="fas fa-trash-alt m-1"></span>
<span class="hidden">Delete</span>
</button>
{#if $lq__hosted_file_obj_li && $lq__hosted_file_obj_li.length}
<div class="overflow-auto w-full">
<button
type="button"
disabled={!$ae_loc.administrator_access}
class:hidden={!$ae_loc.administrator_access}
onclick={() => {
// This should show/hide the editable fields for the file. This might need to be in a little modal.
}}
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
title="Edit file details"
>
<span class="fas fa-edit m-1"></span>
<span class="hidden">Edit</span>
</button>
<span class="text-xs text-gray-500">
<span class="fas fa-calendar-day mx-1"></span>
<span class="hidden">Created on:</span>
{ae_util.iso_datetime_formatter(hosted_file_obj.created_on, 'datetime_medium_sec')}
{#if hosted_file_obj.updated_on}
<span class="fas fa-sync-alt mx-1"></span>
Updated on
{ae_util.iso_datetime_formatter(hosted_file_obj.updated_on, 'datetime_medium_sec')}
{/if}
</span>
<ol class="list-decimal list-inside">
{#each [...$lq__hosted_file_obj_li].reverse().slice(0, max_file_count) as hosted_file_obj}
<li>
<button
type="button"
disabled={!$ae_loc.trusted_access}
onclick={() => {
// This (uploaded_file_kv) is referenced by other AE components. Currently it is only used for the video clipper. This should be a toggle of Add/Remove.
if ($ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id]) {
delete $ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id];
$ae_loc.files.uploaded_file_kv = {...$ae_loc.files.uploaded_file_kv};
delete slct_hosted_file_kv[hosted_file_obj.hosted_file_id];
slct_hosted_file_id = null;
slct_hosted_file_obj = null;
} else {
$ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id] = hosted_file_obj;
lq__hosted_file_obj_li[hosted_file_obj.hosted_file_id] = hosted_file_obj;
slct_hosted_file_kv[hosted_file_obj.hosted_file_id] = hosted_file_obj;
slct_hosted_file_id = hosted_file_obj.hosted_file_id;
slct_hosted_file_obj = hosted_file_obj;
}
log_lvl = 1;
if (log_lvl) {
console.log(`slct_hosted_file_kv:`, slct_hosted_file_kv);
}
}}
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500"
title="Add/Remove file to/from the locally stored uploaded file list. This is referenced by other AE components."
>
{#if $ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id]}
<span class="fas fa-minus-circle m-1"></span>
<span class="hidden">Remove</span>
{:else}
<span class="fas fa-plus-circle m-1"></span>
<span class="hidden">Add</span>
{/if}
</button>
<button
type="button"
disabled={!$ae_loc.administrator_access}
onclick={() => {
if (!confirm(`Are you sure you want to delete this hosted file?\n${hosted_file_obj.filename} [${hosted_file_obj.hosted_file_id_random}]`)) {return false;}
ae_promises.delete__hosted_file_obj = core_func.delete_ae_obj_id__hosted_file({
api_cfg: $ae_api,
hosted_file_id: hosted_file_obj.hosted_file_id_random,
link_to_type: link_to_type,
link_to_id: link_to_id,
rm_orphan: true,
fake_delete: false,
log_lvl: 1
})
}}
class:hidden={!$ae_loc.administrator_access}
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500"
title="Delete a file from the host server."
>
<span class="fas fa-trash-alt m-1"></span>
<span class="hidden">Delete</span>
</button>
<button
type="button"
disabled={!$ae_loc.administrator_access}
class:hidden={!$ae_loc.administrator_access}
onclick={() => {
// This should show/hide the editable fields for the file. This might need to be in a little modal.
}}
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
title="Edit file details"
>
<span class="fas fa-edit m-1"></span>
<span class="hidden">Edit</span>
</button>
<span class="text-xs text-gray-500">
<span class="fas fa-calendar-day mx-1"></span>
<span class="hidden">Created on:</span>
{ae_util.iso_datetime_formatter(hosted_file_obj.created_on, 'datetime_medium_sec')}
{#if hosted_file_obj.updated_on}
<span class="fas fa-sync-alt mx-1"></span>
Updated on
{ae_util.iso_datetime_formatter(hosted_file_obj.updated_on, 'datetime_medium_sec')}
{/if}
</span>
<span class="text-sm font-semibold">
<!-- <a href="" class="underline text-blue-500"> -->
{hosted_file_obj.filename}
<!-- </a> -->
</span>
<span class="text-xs text-gray-500">
{ae_util.format_bytes(hosted_file_obj.size)}
</span>
<span
class:hidden={!$ae_loc.edit_mode}
class="text-xs text-gray-500"
>
{hosted_file_obj.hash_sha256?.slice(0, 5)}...
</span>
</li>
{/each}
</ol>
</div>
{:else}
<p
class="w-96 text-center text-gray-500"
class:hidden={display_mode != 'default'}
>
No files available to display
</p>
{/if}
<span class="text-sm font-semibold">
<!-- <a href="" class="underline text-blue-500"> -->
{hosted_file_obj.filename}
<!-- </a> -->
</span>
<span class="text-xs text-gray-500">
{ae_util.format_bytes(hosted_file_obj.size)}
</span>
<span class:hidden={!$ae_loc.edit_mode} class="text-xs text-gray-500">
{hosted_file_obj.hash_sha256?.slice(0, 5)}...
</span>
</li>
{/each}
</ol>
</div>
{:else}
<p class="w-96 text-center text-gray-500" class:hidden={display_mode != 'default'}>
No files available to display
</p>
{/if}
</section>
<style>
</style>
</style>

View File

@@ -1,128 +1,122 @@
<script lang="ts">
// NEW NEW NEW: 2025-01-07
import { liveQuery } from "dexie";
// NEW NEW NEW: 2025-01-07
import { liveQuery } from 'dexie';
import type { key_val } from '$lib/stores/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import Element_manage_hosted_file_li from '$lib/elements/element_manage_hosted_file_li.svelte';
import type { key_val } from '$lib/stores/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
// import Element_ae_crud from '$lib/element_ae_crud.svelte';
// import Element_data_store from '$lib/element_data_store_v2.svelte';
import Element_manage_hosted_file_li from '$lib/elements/element_manage_hosted_file_li.svelte';
// import { core_func } from '$lib/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_core } from "$lib/ae_core/db_core";
// import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
// import { core_func } from '$lib/ae_core_functions';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_core } from '$lib/ae_core/db_core';
// import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
interface Props {
// import { events_func } from '$lib/ae_events_functions';
class_li_default?: string; // |Array<string>;
class_li?: string; // |Array<string>;
link_to_type: string;
link_to_id: string;
allow_basic?: boolean; // Not used yet
allow_moderator?: boolean; // Not used yet
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
max_file_count?: number;
file_type?: string; // 'image', 'video', 'audio', 'document', 'other'
slct_hosted_file_kv?: key_val;
slct_hosted_file_id?: any;
slct_hosted_file_obj?: any;
}
interface Props {
// import { events_func } from '$lib/ae_events_functions';
class_li_default?: string; // |Array<string>;
class_li?: string; // |Array<string>;
link_to_type: string;
link_to_id: string;
allow_basic?: boolean; // Not used yet
allow_moderator?: boolean; // Not used yet
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
max_file_count?: number;
file_type?: string; // 'image', 'video', 'audio', 'document', 'other'
slct_hosted_file_kv?: key_val;
slct_hosted_file_id?: any;
slct_hosted_file_obj?: any;
}
let {
class_li_default,
class_li,
link_to_type,
link_to_id,
allow_basic = false,
allow_moderator = false,
display_mode = 'default',
max_file_count = 49,
file_type = 'all',
slct_hosted_file_kv = $bindable({}),
slct_hosted_file_id = $bindable(null),
slct_hosted_file_obj = $bindable(null)
}: Props = $props();
let {
class_li_default,
class_li,
link_to_type,
link_to_id,
allow_basic = false,
allow_moderator = false,
display_mode = 'default',
max_file_count = 49,
file_type = 'all',
slct_hosted_file_kv = $bindable({}),
slct_hosted_file_id = $bindable(null),
slct_hosted_file_obj = $bindable(null)
}: Props = $props();
console.log(`HERE HERE HERE HERE: link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
console.log(`HERE HERE HERE HERE: link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
// export let show_convert_btn: null|boolean = null;
// export let show_convert_btn: null|boolean = null;
// let ae_placeholder_li: key_val = {};
// let ae_promises: key_val = {};
let ae_tmp: key_val = {};
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = false;
// let ae_triggers: key_val = {};
// let ae_placeholder_li: key_val = {};
// let ae_promises: key_val = {};
let ae_tmp: key_val = {};
ae_tmp.show__file_li = true;
ae_tmp.show__direct_download = false;
// let ae_triggers: key_val = {};
let dq__where_val: string = `${link_to_type}_id`; // no more _random ???
let dq__where_eq_val: string = link_to_id;
let dq__where_val: string = `${link_to_type}_id`; // no more _random ???
let dq__where_eq_val: string = link_to_id;
// This should include all files that are associated with an object (event, location, session, presenter, etc.)
// I am not sure why, but doing reverse() and then sortBy() seems to sort in descending order.
let lq__hosted_file_obj_li = $derived(liveQuery(async () => {
// console.log(`dq__where_val: ${dq__where_val}`);
// console.log(`dq__where_eq_val: ${dq__where_eq_val}`);
let results = null;
if (file_type == 'all' || !file_type) {
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.sortBy('created_on')
;
} else if (file_type == 'video') {
// Handle video/mp4, video/mov, video/webm. If the content type is prefixed with "video/", then it is a video file.
let extension = 'mp4';
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
// .and((x) => (x.extension == extension))
// .and((x) => (x.content_type == `video/${extension}`))
.and((x) => (x.content_type.startsWith('video/')))
// .reverse()
.sortBy('created_on')
// .toArray()
;
} else if (file_type == 'audio') {
// Handle audio/mp3, audio/wav, audio/ogg. If the content type is prefixed with "audio/", then it is an audio file.
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((x) => (x.content_type.startsWith('audio/')))
.sortBy('created_on')
;
} else if (file_type == 'image') {
// Handle image/jpeg, image/png, image/gif. If the content type is prefixed with "image/", then it is an image file.
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((x) => (x.content_type.startsWith('image/')))
.sortBy('created_on')
;
} else if (file_type == 'document') {
// Handle application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation. If the content type is prefixed with "application/", then it is a document file.
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((x) => (x.content_type.startsWith('application/')))
.sortBy('created_on')
;
} else {
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
// .reverse()
.sortBy('created_on')
;
}
// This should include all files that are associated with an object (event, location, session, presenter, etc.)
// I am not sure why, but doing reverse() and then sortBy() seems to sort in descending order.
let lq__hosted_file_obj_li = $derived(
liveQuery(async () => {
// console.log(`dq__where_val: ${dq__where_val}`);
// console.log(`dq__where_eq_val: ${dq__where_eq_val}`);
let results = null;
if (file_type == 'all' || !file_type) {
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.sortBy('created_on');
} else if (file_type == 'video') {
// Handle video/mp4, video/mov, video/webm. If the content type is prefixed with "video/", then it is a video file.
let extension = 'mp4';
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
// .and((x) => (x.extension == extension))
// .and((x) => (x.content_type == `video/${extension}`))
.and((x) => x.content_type.startsWith('video/'))
// .reverse()
.sortBy('created_on');
// .toArray()
} else if (file_type == 'audio') {
// Handle audio/mp3, audio/wav, audio/ogg. If the content type is prefixed with "audio/", then it is an audio file.
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((x) => x.content_type.startsWith('audio/'))
.sortBy('created_on');
} else if (file_type == 'image') {
// Handle image/jpeg, image/png, image/gif. If the content type is prefixed with "image/", then it is an image file.
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((x) => x.content_type.startsWith('image/'))
.sortBy('created_on');
} else if (file_type == 'document') {
// Handle application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation. If the content type is prefixed with "application/", then it is a document file.
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
.and((x) => x.content_type.startsWith('application/'))
.sortBy('created_on');
} else {
results = await db_core.file
.where(dq__where_val)
.equals(dq__where_eq_val)
// .reverse()
.sortBy('created_on');
}
return results;
}));
return results;
})
);
</script>
<!-- {#if lq__hosted_file_obj_li}
<h3 class="h3">{lq__hosted_file_obj_li.length}&times; files clipped</h3>
<div class="container">
@@ -138,23 +132,22 @@ let lq__hosted_file_obj_li = $derived(liveQuery(async () => {
<p>No files found</p>
{/if} -->
{#if lq__hosted_file_obj_li}
<Element_manage_hosted_file_li
link_to_type={link_to_type}
link_to_id={link_to_id}
lq__hosted_file_obj_li={lq__hosted_file_obj_li}
class_li_default={class_li_default}
class_li={class_li}
display_mode={display_mode}
bind:max_file_count={max_file_count}
bind:file_type={file_type}
bind:slct_hosted_file_kv={slct_hosted_file_kv}
bind:slct_hosted_file_id={slct_hosted_file_id}
bind:slct_hosted_file_obj={slct_hosted_file_obj}
/>
<!-- allow_basic={allow_basic} -->
<!-- allow_moderator={allow_moderator} -->
<Element_manage_hosted_file_li
{link_to_type}
{link_to_id}
{lq__hosted_file_obj_li}
{class_li_default}
{class_li}
{display_mode}
bind:max_file_count
bind:file_type
bind:slct_hosted_file_kv
bind:slct_hosted_file_id
bind:slct_hosted_file_obj
/>
<!-- allow_basic={allow_basic} -->
<!-- allow_moderator={allow_moderator} -->
{:else}
<p>No files found</p>
<p>No files found</p>
{/if}

View File

@@ -1,54 +1,49 @@
<script lang="ts">
import { onMount } from 'svelte';
import { onMount } from 'svelte';
// This (ae) is only used for utilities
// import { ae } from 'aether_npm_lib';
import { ae_util } from '$lib/ae_utils/ae_utils';
// This (ae) is only used for utilities
// import { ae } from 'aether_npm_lib';
import { ae_util } from '$lib/ae_utils/ae_utils';
// These (slct_*) are only used internally for this component. Not needed???
// import { slct_obj_id, slct_obj_li_type, slct_obj_type } from '../admin/stores_admin.js';
// These (slct_*) are only used internally for this component. Not needed???
// import { slct_obj_id, slct_obj_li_type, slct_obj_type } from '../admin/stores_admin.js';
// Should these slct_* be exported???
let slct_obj_id = null;
let slct_obj_li_type = null;
let slct_obj_type = null;
// Should these slct_* be exported???
let slct_obj_id = null;
let slct_obj_li_type = null;
let slct_obj_type = null;
export let row_header: boolean = false;
export let primary_obj_li_type: string = slct_obj_li_type; // account, person, user, event, event_session, membership_person
export let obj = null;
console.log(obj);
console.log(typeof obj);
export let row_header: boolean = false;
export let primary_obj_li_type: string = slct_obj_li_type; // account, person, user, event, event_session, membership_person
export let obj = null;
console.log(obj);
console.log(typeof obj);
onMount(() => {
console.log('** Element Mounted: ** Element Object Table Row');
onMount(() => {
console.log('** Element Mounted: ** Element Object Table Row');
if (obj) {
console.log('Table Row Object:', obj);
// console.log(typeof obj);
} else {
return false;
}
});
if (obj) {
console.log('Table Row Object:', obj);
// console.log(typeof obj);
} else {
return false;
}
/* BEGIN: Handle requests (archive, create, hide, remove, select, update, POST, PATCH, GET, DELETE) */
/* END: Handle requests (archive, create, hide, remove, select, update, POST, PATCH, GET, DELETE) */
});
/* BEGIN: Handle other local actions (show/hide form, process data) */
/* END: Handle other local actions (show/hide form, process data) */
/* BEGIN: Handle requests (archive, create, hide, remove, select, update, POST, PATCH, GET, DELETE) */
/* END: Handle requests (archive, create, hide, remove, select, update, POST, PATCH, GET, DELETE) */
/* BEGIN: Handle other local actions (show/hide form, process data) */
/* END: Handle other local actions (show/hide form, process data) */
/* BEGIN: Handle children events (archived, canceled, closed, created, deleted, hidden, updated) */
/* END: Handle children events (archived, canceled, closed, created, deleted, hidden, updated) */
/* BEGIN: Handle children events (archived, canceled, closed, created, deleted, hidden, updated) */
/* END: Handle children events (archived, canceled, closed, created, deleted, hidden, updated) */
</script>
<tr>
{#if obj != null && typeof obj == 'object'}
{#each Object.entries(obj) as [obj_prop_name, obj_prop_value]}
<!-- NEED TO ADD A CHECK IF:
{#if obj != null && typeof obj == 'object'}
{#each Object.entries(obj) as [obj_prop_name, obj_prop_value]}
<!-- NEED TO ADD A CHECK IF:
NOTE:
NOTE:
@@ -60,91 +55,105 @@ onMount(() => {
NOTE:
-->
{#if obj_prop_name.endsWith('_id_random') || obj_prop_name == 'for_type' || obj_prop_name == 'for_id'}
{#if row_header}
<th data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name} on:click={() => primary_obj_li_type=obj_prop_name.replace('_id_random', '')}>
{ae_util.set_obj_prop_display_name({prop_name: obj_prop_name, obj_type: primary_obj_li_type})}
</th>
{:else}
<td
data-obj_type={primary_obj_li_type}
data-obj_prop_name={obj_prop_name}
on:click={() => {
slct_obj_type=obj_prop_name.replace('_id_random', '');
slct_obj_id=obj_prop_value;
}}
on:keypress={() => {
slct_obj_type=obj_prop_name.replace('_id_random', '');
slct_obj_id=obj_prop_value;
}}
>
<!-- {obj_prop_value} -->
{#if obj_prop_name.endsWith('_id_random') || obj_prop_name == 'for_type' || obj_prop_name == 'for_id'}
{#if row_header}
<th
data-obj_type={primary_obj_li_type}
data-obj_prop_name={obj_prop_name}
on:click={() => (primary_obj_li_type = obj_prop_name.replace('_id_random', ''))}
>
{ae_util.set_obj_prop_display_name({
prop_name: obj_prop_name,
obj_type: primary_obj_li_type
})}
</th>
{:else}
<td
data-obj_type={primary_obj_li_type}
data-obj_prop_name={obj_prop_name}
on:click={() => {
slct_obj_type = obj_prop_name.replace('_id_random', '');
slct_obj_id = obj_prop_value;
}}
on:keypress={() => {
slct_obj_type = obj_prop_name.replace('_id_random', '');
slct_obj_id = obj_prop_value;
}}
>
<!-- {obj_prop_value} -->
<!-- {#if (obj_prop_value && obj_prop_value.length > 25)}
<!-- {#if (obj_prop_value && obj_prop_value.length > 25)}
{obj_prop_value.substring(0,25)}
{:else} -->
{#if obj_prop_value}
<a href="/{ae_util.return_obj_type_path({obj_type_prop_name: obj_prop_name})}/{obj_prop_value}">
{obj_prop_value.substring(0,25)}
</a>
{:else}
<!-- {obj_prop_value} -->
<span class="fs_smaller">-- None --</span>
{/if}
</td>
{/if}
{:else if obj_prop_name.endsWith('[URL]')}
{#if row_header}
<th data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name} on:click={() => primary_obj_li_type=obj_prop_name.replaceAll('[URL]', '')}>
{ae_util.set_obj_prop_display_name({prop_name: obj_prop_name.replaceAll('[URL]', ''), obj_type: primary_obj_li_type})}
</th>
{:else}
<td
data-obj_type={primary_obj_li_type}
data-obj_prop_name={obj_prop_name}
on:click={() => {
slct_obj_type=obj_prop_name.replaceAll('[URL]', '');
slct_obj_id=obj_prop_value;
}}
on:keypress={() => {
slct_obj_type=obj_prop_name.replaceAll('[URL]', '');
slct_obj_id=obj_prop_value;
}}
>
<a href="{obj_prop_value}">{obj_prop_value}</a>
</td>
{/if}
{:else}
{#if row_header}
<th data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>
{ae_util.set_obj_prop_display_name({prop_name: obj_prop_name, obj_type: primary_obj_li_type})}
</th>
{:else}
<td data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>
{#if (obj_prop_value)}
{#if (obj_prop_value && obj_prop_value.length > 25)}
{obj_prop_value.substring(0,25)} ...
{:else}
{obj_prop_value}
{/if}
{:else}
<span class="fs_smaller">-- None --</span>
{/if}
</td>
{/if}
{/if}
<!-- <td data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>{obj_prop_value}</td> -->
{/each}
{:else}
<!-- This should never happen -->
<td class="fs_smaller" colspan="100"
>
-- Not Set --
</td>
{/if}
{#if obj_prop_value}
<a
href="/{ae_util.return_obj_type_path({
obj_type_prop_name: obj_prop_name
})}/{obj_prop_value}"
>
{obj_prop_value.substring(0, 25)}
</a>
{:else}
<!-- {obj_prop_value} -->
<span class="fs_smaller">-- None --</span>
{/if}
</td>
{/if}
{:else if obj_prop_name.endsWith('[URL]')}
{#if row_header}
<th
data-obj_type={primary_obj_li_type}
data-obj_prop_name={obj_prop_name}
on:click={() => (primary_obj_li_type = obj_prop_name.replaceAll('[URL]', ''))}
>
{ae_util.set_obj_prop_display_name({
prop_name: obj_prop_name.replaceAll('[URL]', ''),
obj_type: primary_obj_li_type
})}
</th>
{:else}
<td
data-obj_type={primary_obj_li_type}
data-obj_prop_name={obj_prop_name}
on:click={() => {
slct_obj_type = obj_prop_name.replaceAll('[URL]', '');
slct_obj_id = obj_prop_value;
}}
on:keypress={() => {
slct_obj_type = obj_prop_name.replaceAll('[URL]', '');
slct_obj_id = obj_prop_value;
}}
>
<a href={obj_prop_value}>{obj_prop_value}</a>
</td>
{/if}
{:else if row_header}
<th data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>
{ae_util.set_obj_prop_display_name({
prop_name: obj_prop_name,
obj_type: primary_obj_li_type
})}
</th>
{:else}
<td data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>
{#if obj_prop_value}
{#if obj_prop_value && obj_prop_value.length > 25}
{obj_prop_value.substring(0, 25)} ...
{:else}
{obj_prop_value}
{/if}
{:else}
<span class="fs_smaller">-- None --</span>
{/if}
</td>
{/if}
<!-- <td data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>{obj_prop_value}</td> -->
{/each}
{:else}
<!-- This should never happen -->
<td class="fs_smaller" colspan="100"> -- Not Set -- </td>
{/if}
</tr>
<style lang="postcss">
</style>
</style>

View File

@@ -1,145 +1,146 @@
<script lang="ts">
import { onMount } from 'svelte';
import { onMount } from 'svelte';
// *** Import Aether core variables and functions
import type { key_val } from '$lib/stores/ae_stores';
// import { api, Element_obj_tbl_row } from 'aether_npm_lib';
import Element_obj_tbl_row from '$lib/elements/element_obj_tbl_row.svelte';
import { post_object } from '$lib/ae_api/api_post_object';
// *** Import Aether core variables and functions
import type { key_val } from '$lib/stores/ae_stores';
// import { api, Element_obj_tbl_row } from 'aether_npm_lib';
import Element_obj_tbl_row from '$lib/elements/element_obj_tbl_row.svelte';
import { post_object } from '$lib/ae_api/api_post_object';
// *** Import Aether core components
// import Element_obj_tbl_row from './element_obj_tbl_row.svelte';
// *** Import Aether core components
// import Element_obj_tbl_row from './element_obj_tbl_row.svelte';
// *** Import Aether module variables and functions
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Import Aether module components
// *** Export/Exposed variables and functions for component
export let api_cfg: any;
export let show_textarea = true;
export let button_label = 'Run SQL!';
export let show_record_count = true;
export let remove_breaks = false;
export let run_on_load = false;
// *** Export/Exposed variables and functions for component
export let api_cfg: any;
export let show_textarea = true;
export let button_label = 'Run SQL!';
export let show_record_count = true;
export let remove_breaks = false;
export let run_on_load = false;
export let sql_statement: string;
export let sql_data = null;
export let as_list = false;
export let log_lvl: number = 0;
export let sql_statement: string;
export let sql_data = null;
export let as_list = false;
export let log_lvl: number = 0;
// *** Set initial variables
let ae_promises: key_val = {};
let sql_qry_result: any = null;
// *** Set initial variables
let ae_promises: key_val = {};
let sql_qry_result: any = null;
onMount(() => {
console.log('** Element Mounted: ** Element SQL Query');
onMount(() => {
console.log('** Element Mounted: ** Element SQL Query');
if (run_on_load) {
console.log('Run On Load');
let result = handle_run_sql(sql_statement, sql_data, as_list, log_lvl).then((qry_result) => {
console.log('SQL Query Result:', qry_result);
sql_qry_result = qry_result;
return qry_result;
});
}
});
if (run_on_load) {
console.log('Run On Load');
let result = handle_run_sql(sql_statement, sql_data, as_list, log_lvl)
.then((qry_result) => {
console.log('SQL Query Result:', qry_result);
sql_qry_result = qry_result;
return qry_result;
});
}
});
// const dispatch = createEventDispatcher();
// const dispatch = createEventDispatcher();
async function handle_run_sql(qry, data, as_list = false, log_lvl = 0) {
console.log('*** handle_run_sql() ***');
async function handle_run_sql(qry, data, as_list=false, log_lvl=0) {
console.log('*** handle_run_sql() ***');
let sql_qry_data: key_val = {};
let sql_qry_data: key_val = {};
let endpoint = '/sql/select';
let params: key_val = {};
if (as_list) {
params['as_list'] = true;
}
if (log_lvl) {
console.log('Params:', params);
}
let endpoint = '/sql/select';
let params: key_val = {};
if (as_list) {
params['as_list'] = true;
}
if (log_lvl) {
console.log('Params:', params);
}
if (qry) {
} else {
return false;
}
if (log_lvl > 1) {
console.log('Qry:', qry);
}
if (qry) {
} else {
return false;
}
if (log_lvl>1) {
console.log('Qry:', qry);
}
if (remove_breaks) {
sql_qry_data['sql_qry'] = qry.replace(/(\r\n|\n|\r)/gm, '');
} else {
sql_qry_data['sql_qry'] = qry;
}
if (remove_breaks) {
sql_qry_data['sql_qry'] = qry.replace(/(\r\n|\n|\r)/gm, "");
} else {
sql_qry_data['sql_qry'] = qry;
}
sql_qry_data['sql_data'] = data;
sql_qry_data['sql_data'] = data;
if (log_lvl) {
console.log('SQL Qry Data:', sql_qry_data);
}
if (log_lvl) {
console.log('SQL Qry Data:', sql_qry_data);
}
ae_promises.sql_qry_promise = await post_object({
api_cfg: api_cfg,
endpoint: endpoint,
params: params,
data: sql_qry_data,
log_lvl: log_lvl
});
ae_promises.sql_qry_promise = await post_object({api_cfg: api_cfg, endpoint: endpoint, params: params, data: sql_qry_data, log_lvl: log_lvl});
if (log_lvl) {
console.log('SQL Query Results', ae_promises.sql_qry_promise);
}
if (log_lvl) {
console.log('SQL Query Results', ae_promises.sql_qry_promise);
}
return ae_promises.sql_qry_promise;
}
return ae_promises.sql_qry_promise;
}
</script>
<section id="sql_qry" class="sql_qry">
{#if show_textarea}
<textarea
class="textarea"
bind:value={sql_statement}></textarea>
{/if}
{#if show_textarea}
<textarea class="textarea" bind:value={sql_statement}></textarea>
{/if}
<div class="text-center">
<button
type="button"
on:click={async () => {
sql_qry_result = await handle_run_sql(sql_statement, sql_data, as_list, log_lvl);
}}
class="btn btn-md preset-tonal-primary hover:preset-tonal-primary border border-primary-500"
>
{button_label}
</button>
</div>
<div class="text-center">
<button
type="button"
on:click={async () => {
sql_qry_result = await handle_run_sql(sql_statement, sql_data, as_list, log_lvl);
}}
class="btn btn-md preset-tonal-primary hover:preset-tonal-primary border border-primary-500"
>
{button_label}
</button>
</div>
{#if show_record_count && sql_qry_result && sql_qry_result.length}
<div>
Record count: <strong>{sql_qry_result.length}</strong>
</div>
{/if}
{#if show_record_count && sql_qry_result && sql_qry_result.length}
<div>
Record count: <strong>{sql_qry_result.length}</strong>
</div>
{/if}
<div class="sql_statement_result">
{#await ae_promises.sql_qry_promise}
Getting results...
{:then}
{#if sql_qry_result && sql_qry_result.length}
<table class="table table-compact table-bordered table-striped min-w-min sql_qry_result text-xs">
<Element_obj_tbl_row row_header={true} obj={sql_qry_result[0]} primary_obj_li_type='' />
{#each sql_qry_result as record}
<Element_obj_tbl_row obj={record} primary_obj_li_type='' />
{/each}
</table>
{:else}
<div>Nothing to show yet...</div>
{/if}
{/await}
</div>
<div class="sql_statement_result">
{#await ae_promises.sql_qry_promise}
Getting results...
{:then}
{#if sql_qry_result && sql_qry_result.length}
<table
class="table table-compact table-bordered table-striped min-w-min sql_qry_result text-xs"
>
<Element_obj_tbl_row row_header={true} obj={sql_qry_result[0]} primary_obj_li_type="" />
{#each sql_qry_result as record}
<Element_obj_tbl_row obj={record} primary_obj_li_type="" />
{/each}
</table>
{:else}
<div>Nothing to show yet...</div>
{/if}
{/await}
</div>
</section>
<style lang="postcss">
/* .sql_qry textarea {
/* .sql_qry textarea {
width: 100%;
height: 8em;
} */

View File

@@ -1,113 +1,113 @@
/* Basic editor styles */
.tiptap {
:first-child {
margin-top: 0;
}
:first-child {
margin-top: 0;
}
/* Link styles */
a {
color: var(--purple);
cursor: pointer;
text-decoration: underline;
/* Link styles */
a {
color: var(--purple);
cursor: pointer;
text-decoration: underline;
&:hover {
color: var(--purple-contrast);
}
}
&:hover {
color: var(--purple-contrast);
}
}
/* List styles */
ul {
list-style-type: disc;
margin-left: 1.5rem;
// border: solid thin red;
}
ol {
list-style-type: decimal;
margin-left: 1.5rem;
// border: solid thin red;
}
/* List styles */
ul {
list-style-type: disc;
margin-left: 1.5rem;
// border: solid thin red;
}
ol {
list-style-type: decimal;
margin-left: 1.5rem;
// border: solid thin red;
}
ul,
ol {
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
ul,
ol {
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
}
li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
}
/* Heading styles */
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
margin-top: 2.5rem;
text-wrap: pretty;
}
/* Heading styles */
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
margin-top: 2.5rem;
text-wrap: pretty;
}
h1,
h2 {
margin-top: 3.5rem;
margin-bottom: 1.5rem;
}
h1,
h2 {
margin-top: 3.5rem;
margin-bottom: 1.5rem;
}
h1 {
font-size: 1.4rem;
}
h1 {
font-size: 1.4rem;
}
h2 {
font-size: 1.2rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
font-size: 1.1rem;
}
h3 {
font-size: 1.1rem;
}
h4,
h5,
h6 {
font-size: 1rem;
}
h4,
h5,
h6 {
font-size: 1rem;
}
/* Code and preformatted text styles */
code {
background-color: var(--purple-light);
border-radius: 0.4rem;
color: var(--black);
font-size: 0.85rem;
padding: 0.25em 0.3em;
}
/* Code and preformatted text styles */
code {
background-color: var(--purple-light);
border-radius: 0.4rem;
color: var(--black);
font-size: 0.85rem;
padding: 0.25em 0.3em;
}
pre {
background: var(--black);
border-radius: 0.5rem;
color: var(--white);
font-family: 'JetBrainsMono', monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
pre {
background: var(--black);
border-radius: 0.5rem;
color: var(--white);
font-family: 'JetBrainsMono', monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
}
}
code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
}
}
blockquote {
border-left: 3px solid var(--gray-3);
margin: 1.5rem 0;
padding-left: 1rem;
}
blockquote {
border-left: 3px solid var(--gray-3);
margin: 1.5rem 0;
padding-left: 1rem;
}
hr {
border: none;
border-top: 1px solid var(--gray-2);
margin: 2rem 0;
}
}
hr {
border: none;
border-top: 1px solid var(--gray-2);
margin: 2rem 0;
}
}

View File

@@ -1,160 +1,155 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
// import { fade } from 'svelte/transition'
// import { cubicOut } from 'svelte/easing';
import { onMount, onDestroy } from 'svelte';
// import { fade } from 'svelte/transition'
// import { cubicOut } from 'svelte/easing';
import ShadEditor from '$lib/components/shad-editor/shad-editor.svelte';
import ShadEditor from '$lib/components/shad-editor/shad-editor.svelte';
// Import Tiptap related modules
// import { Editor } from "@tiptap/core";
// import StarterKit from "@tiptap/starter-kit";
// import Bold from '@tiptap/extension-bold';
// import BulletList from '@tiptap/extension-bullet-list';
// import CodeBlock from '@tiptap/extension-code-block';
// import Code from '@tiptap/extension-code';
// import Color from '@tiptap/extension-color';
// import Document from '@tiptap/extension-document';
// import Heading from '@tiptap/extension-heading';
// import Highlight from '@tiptap/extension-highlight';
// import History from '@tiptap/extension-history';
// import Italic from '@tiptap/extension-italic';
// import Link from '@tiptap/extension-link';
// import ListItem from '@tiptap/extension-list-item';
// import OrderedList from '@tiptap/extension-ordered-list';
// import Paragraph from '@tiptap/extension-paragraph';
// import Strike from '@tiptap/extension-strike';
// import Text from '@tiptap/extension-text';
// import TextStyle from '@tiptap/extension-text-style';
// import Typography from '@tiptap/extension-typography';
// import Underline from '@tiptap/extension-underline';
// Import Tiptap related modules
// import { Editor } from "@tiptap/core";
// import StarterKit from "@tiptap/starter-kit";
// import Bold from '@tiptap/extension-bold';
// import BulletList from '@tiptap/extension-bullet-list';
// import CodeBlock from '@tiptap/extension-code-block';
// import Code from '@tiptap/extension-code';
// import Color from '@tiptap/extension-color';
// import Document from '@tiptap/extension-document';
// import Heading from '@tiptap/extension-heading';
// import Highlight from '@tiptap/extension-highlight';
// import History from '@tiptap/extension-history';
// import Italic from '@tiptap/extension-italic';
// import Link from '@tiptap/extension-link';
// import ListItem from '@tiptap/extension-list-item';
// import OrderedList from '@tiptap/extension-ordered-list';
// import Paragraph from '@tiptap/extension-paragraph';
// import Strike from '@tiptap/extension-strike';
// import Text from '@tiptap/extension-text';
// import TextStyle from '@tiptap/extension-text-style';
// import Typography from '@tiptap/extension-typography';
// import Underline from '@tiptap/extension-underline';
import './element_tiptap_editor.scss';
import './element_tiptap_editor.scss';
// https://tiptap.dev/docs/examples/basics/default-text-editor
// https://tiptap.dev/docs/examples/basics/formatting
// <code class="language-css">
// https://tiptap.dev/docs/examples/basics/default-text-editor
// https://tiptap.dev/docs/examples/basics/formatting
// <code class="language-css">
export let html_text: string = '';
export let default_minimal: boolean = false;
export let show_toolbar: boolean = true;
export let placeholder: string = 'Type your text here...';
export let changed: boolean = false;
export let html_text: string = '';
export let default_minimal: boolean = false;
export let show_toolbar: boolean = true;
export let placeholder: string = 'Type your text here...';
export let changed: boolean = false;
export let classes: string = '';
export let classes: string = '';
if (default_minimal) {
show_toolbar = false;
}
if (default_minimal) {
show_toolbar = false;
}
// export let html_text: string = `
// <h2>
// Hi there,
// </h2>
// <p>
// this is a <em>basic</em> example of <strong>Tiptap</strong>. Sure, there are all kind of basic text styles youd probably expect from a text editor. But wait until you see the lists:
// </p>
// <ul>
// <li>
// Thats a bullet list with one …
// </li>
// <li>
// … or two list items.
// </li>
// </ul>
// <p>
// Isnt that great? And all of that is editable. But wait, theres more. Lets try a code block:
// </p>
// <pre><code class="language-css">body {
// display: none;
// }</code></pre>
// <p>
// I know, I know, this is impressive. Its only the tip of the iceberg though. Give it a try and click a little bit around. Dont forget to check the other examples too.
// </p>
// <blockquote>
// Wow, thats amazing. Good work, boy! 👏
// <br />
// — Mom
// </blockquote>
// `;
// export let html_text: string = `
// <h2>
// Hi there,
// </h2>
// <p>
// this is a <em>basic</em> example of <strong>Tiptap</strong>. Sure, there are all kind of basic text styles youd probably expect from a text editor. But wait until you see the lists:
// </p>
// <ul>
// <li>
// Thats a bullet list with one …
// </li>
// <li>
// … or two list items.
// </li>
// </ul>
// <p>
// Isnt that great? And all of that is editable. But wait, theres more. Lets try a code block:
// </p>
// <pre><code class="language-css">body {
// display: none;
// }</code></pre>
// <p>
// I know, I know, this is impressive. Its only the tip of the iceberg though. Give it a try and click a little bit around. Dont forget to check the other examples too.
// </p>
// <blockquote>
// Wow, thats amazing. Good work, boy! 👏
// <br />
// — Mom
// </blockquote>
// `;
let element: HTMLDivElement;
let editor: any;
// More default options should be defined later.
// minimal, basic, full
let element: HTMLDivElement;
let editor: any;
// More default options should be defined later.
// minimal, basic, full
export let show_button_kv: any;
export let show_button_kv: any;
// export let new_json = editor?.getJSON();
export let new_html: string = '';
let orig_html: string = html_text;
// export let new_json = editor?.getJSON();
export let new_html: string = '';
let orig_html: string = html_text;
onMount(() => {});
onMount(() => {
});
onDestroy(() => {});
onDestroy(() => {
});
$: if (html_text !== orig_html && html_text !== '<p></p>') {
console.log('html_text changed:', html_text);
console.log('orig_html:', orig_html);
changed = true;
} else {
changed = false;
}
$: if (html_text !== orig_html && html_text !== '<p></p>') {
console.log('html_text changed:', html_text);
console.log('orig_html:', orig_html);
changed = true;
} else {
changed = false;
}
let mouse_entered_timer: any;
let mouse_enter_wait: number = 500;
let mouse_leave_wait: number = 2000;
let mouse_entered_timer: any;
let mouse_enter_wait: number = 500;
let mouse_leave_wait: number = 2000;
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
on:click={() => {
if (default_minimal) {
show_toolbar = true;
}
}}
on:mouseleave={() => {
clearTimeout(mouse_entered_timer);
on:click={() => {
if (default_minimal) {
show_toolbar = true;
}
}}
on:mouseleave={() => {
clearTimeout(mouse_entered_timer);
mouse_entered_timer = setTimeout(() => {
if (default_minimal) {
show_toolbar = false;
}
}, mouse_leave_wait);
}}
on:mouseenter={() => {
clearTimeout(mouse_entered_timer);
mouse_entered_timer = setTimeout(() => {
if (default_minimal) {
show_toolbar = false;
}
}, mouse_leave_wait);
}}
on:mouseenter={() => {
clearTimeout(mouse_entered_timer);
mouse_entered_timer = setTimeout(() => {
if (default_minimal) {
show_toolbar = true;
}
}, mouse_enter_wait);
}}
class="block w-full h-full {classes}"
>
<ShadEditor
class="p-1 transition-all duration-1000"
bind:content={html_text}
bind:new_html={new_html}
placeholder={placeholder}
show_toolbar={show_toolbar}
show_button_kv={show_button_kv}
/>
mouse_entered_timer = setTimeout(() => {
if (default_minimal) {
show_toolbar = true;
}
}, mouse_enter_wait);
}}
class="block w-full h-full {classes}"
>
<ShadEditor
class="p-1 transition-all duration-1000"
bind:content={html_text}
bind:new_html
{placeholder}
{show_toolbar}
{show_button_kv}
/>
</div>
<style lang="css">
/*
/*
// :global(.ProseMirror) {
// padding: .25em;
// }
*/
:global(.ProseMirror-focused) {
outline: none;
}
:global(.ProseMirror-focused) {
outline: none;
}
</style>

File diff suppressed because it is too large Load Diff