-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
830 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// SPDX-FileCopyrightText: 2024 caixw | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
import { createMemo, For, JSX, mergeProps, onMount, Show } from 'solid-js'; | ||
|
||
import { Accessor } from '@/components/form'; | ||
import { PreviewFile, PreviewURL } from './preview'; | ||
import Upload, { Props as BaseProps, Ref } from './upload'; | ||
|
||
export interface Props extends Omit<BaseProps,'dropzone'|'ref'> { | ||
/** | ||
* 是否接受直接拖入文件 | ||
* | ||
* 非响应式的属性 | ||
*/ | ||
droppable?: boolean; | ||
|
||
/** | ||
* 是否自动执行上传操作 | ||
*/ | ||
auto?: boolean; | ||
|
||
/** | ||
* 逆向显示内容,这将会导致上传按钮显示在最前面。 | ||
*/ | ||
reverse?: boolean; | ||
|
||
/** | ||
* 保存着所有已经上传的文件列表 | ||
*/ | ||
accessor: Accessor<Array<string>>; | ||
|
||
/** | ||
* 子项的宽度 | ||
*/ | ||
itemSize?: string; | ||
} | ||
|
||
const presetProps: Readonly<Partial<Props>> = { | ||
itemSize: '72px', | ||
}; | ||
|
||
export default function(props: Props): JSX.Element { | ||
props = mergeProps(presetProps, props); | ||
const access = props.accessor; | ||
|
||
let dropRef: HTMLDivElement; | ||
let uploadRef: Ref; | ||
|
||
onMount(()=>{ | ||
if (!props.droppable) { | ||
dropRef.addEventListener('dragover', (e)=>{ | ||
e.dataTransfer!.dropEffect = 'none'; | ||
e.preventDefault(); | ||
}); | ||
return; | ||
} | ||
}); | ||
|
||
const size = createMemo((): JSX.CSSProperties => { | ||
return { 'height': props.itemSize, 'width': props.itemSize }; | ||
}); | ||
|
||
return <fieldset disabled={props.disabled} class={props.class} classList={{ | ||
...props.classList, | ||
'c--upload': true, | ||
'c--field': true, | ||
[`palette--${props.palette}`]: !!props.palette, | ||
}}> | ||
<div> | ||
{props.label} | ||
<div ref={el => dropRef = el} classList={{ | ||
'content': true, | ||
}}> | ||
<Upload ref={el => uploadRef = el} fieldName={props.fieldName} multiple={props.multiple} action={props.action} | ||
accept={props.accept} dropzone={dropRef!} /> | ||
|
||
<For each={access.getValue()}> | ||
{(item) => ( | ||
<PreviewURL size={props.itemSize!} url={item} del={() => { | ||
access.setValue(access.getValue().filter((v) => v !== item)); | ||
}} /> | ||
)} | ||
</For> | ||
|
||
<For each={uploadRef!.files()}> | ||
{(item, index) => { | ||
return <PreviewFile size={props.itemSize!} file={item} del={() => { | ||
uploadRef.delete(index()); | ||
}} />; | ||
}} | ||
</For> | ||
<Show when={props.auto && (props.multiple || (access.getValue().length + uploadRef!.files().length) === 0)}> | ||
<button style={size()} class={'c--icon action' + (props.reverse ? ' start' : '')} onClick={async () => { | ||
uploadRef.pick(); | ||
await uploadRef.upload(); | ||
}}>upload_file</button> | ||
</Show> | ||
<Show when={!props.auto}> | ||
<Show when={(props.multiple || (access.getValue().length + uploadRef!.files().length) === 0)}> | ||
<button style={size()} class={'c--icon action' + (props.reverse ? ' start' : '')} onClick={() => uploadRef.pick()}>add</button> | ||
</Show> | ||
<Show when={uploadRef!.files().length > 0}> | ||
<button style={size()} class={'c--icon action' + (props.reverse ? ' start' : '')} onClick={() => uploadRef!.upload()}>upload</button> | ||
</Show> | ||
</Show> | ||
</div> | ||
</div> | ||
|
||
<Show when={access.hasError()}> | ||
<p class="field_error" role="alert">{access.getError()}</p> | ||
</Show> | ||
</fieldset>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// SPDX-FileCopyrightText: 2024 caixw | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
import { JSX } from 'solid-js'; | ||
|
||
import { useOptions } from '@/app/context'; | ||
import { boolSelector, Demo, paletteSelector, Stage } from '@/components/base/demo'; | ||
import { FieldAccessor } from '../access'; | ||
import { default as Album } from './album'; | ||
|
||
export default function(): JSX.Element { | ||
const opt = useOptions(); | ||
|
||
const [paletteS, palette] = paletteSelector('secondary'); | ||
const [disabledS, disabled] = boolSelector('disabled'); | ||
const [reverseS, reverse] = boolSelector('reverse'); | ||
const [autoS, auto] = boolSelector('auto'); | ||
|
||
const basicA = FieldAccessor('upload', [opt.logo, './test.jpg'], true); | ||
|
||
return <Demo settings={ | ||
<> | ||
{paletteS} | ||
{disabledS} | ||
{reverseS} | ||
{autoS} | ||
<button class="c--button c--button-fill palette--primary" onClick={() => basicA.setError(basicA.getError() ? undefined : 'error')}>toggle error</button> | ||
</> | ||
} stages={ | ||
<> | ||
<Stage title='basic'> | ||
<Album label="label" class='min-w-16' reverse={reverse()} disabled={disabled()} palette={palette()} auto={auto()} | ||
action='./' accessor={basicA} /> | ||
</Stage> | ||
|
||
<Stage title='basic+drop'> | ||
<Album class='min-w-16' reverse={reverse()} disabled={disabled()} palette={palette()} droppable auto={auto()} | ||
action='./' accessor={basicA} /> | ||
</Stage> | ||
</> | ||
} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-FileCopyrightText: 2024 caixw | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
export { default as Upload } from './upload'; | ||
export type { Props as UploadProps, Ref as UploadRef } from './upload'; | ||
|
||
export { default as Album } from './album'; | ||
export type { Props as AlbumProps } from './album'; | ||
|
||
export { file2Base64 } from './preview'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// SPDX-FileCopyrightText: 2024 caixw | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
import { createEffect, createSignal, JSX } from 'solid-js'; | ||
|
||
import { Icon } from '@/components/icon'; | ||
|
||
export interface URLProps { | ||
size: string; | ||
url: string; | ||
del: {():void;}; | ||
} | ||
|
||
/** | ||
* 根据 URL 生成的预览图 | ||
*/ | ||
export function PreviewURL(props: URLProps): JSX.Element { | ||
return <div class="preview" style={{ | ||
'width': props.size, | ||
'height': props.size, | ||
'background-image': isImageURL(props.url) ? props.url : '', | ||
'background-size': '100% 100%', | ||
}}> | ||
<Icon class="close" icon="close" onClick={props.del} /> | ||
</div>; | ||
} | ||
|
||
export interface FileProps { | ||
size: string; | ||
file: File; | ||
del: {():void;}; | ||
} | ||
|
||
/** | ||
* 根据 {@link File} 生成的预览图 | ||
*/ | ||
export function PreviewFile(props: FileProps): JSX.Element { | ||
const [bg, setBG] = createSignal<string>(''); | ||
|
||
createEffect(async () => { | ||
if (props.file.type.startsWith('image/')) { | ||
setBG('url("'+await file2Base64(props.file) as string)+'")'; | ||
} else { | ||
setBG(''); | ||
} | ||
}); | ||
|
||
return <div class="preview" style={{ | ||
'width': props.size, | ||
'height': props.size, | ||
'background-image': bg(), | ||
'background-size': '100% 100%', | ||
}}> | ||
<Icon class="close" icon="close" onClick={props.del} /> | ||
</div>; | ||
} | ||
|
||
export function file2Base64(file: File): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
const reader = new FileReader(); | ||
reader.readAsDataURL(file); | ||
reader.onload = () => resolve(reader.result as string); | ||
reader.onerror = reject; | ||
}); | ||
} | ||
|
||
const imageExts: ReadonlyArray<string> = [ | ||
'.jpg','.jpeg','.png','.bmp','.ico','.svg', | ||
]; | ||
|
||
function isImageURL(url: string): boolean { | ||
const index = url.lastIndexOf('.'); | ||
if (index === -1 || (index === url.length - 1)) { | ||
return false; | ||
} | ||
|
||
return imageExts.includes(url.slice(index)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 caixw | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
@layer components { | ||
.c--upload { | ||
.content { | ||
@apply w-fit h-fit border rounded-md border-palette-fg-low; | ||
@apply flex flex-wrap gap-2 p-2 items-center justify-start content-start; | ||
|
||
.preview { | ||
@apply relative border border-palette-bg-low p-1 rounded-sm order-10; | ||
|
||
.close { | ||
@apply absolute right-1 top-1 text-palette-fg; | ||
@apply hover:text-palette-fg-high hover:cursor-pointer; | ||
} | ||
} | ||
|
||
.action { | ||
@apply relative border border-palette-bg-low p-1 rounded-sm order-11; | ||
@apply text-2xl text-palette-fg-low; | ||
} | ||
|
||
.action.start { | ||
@apply !order-1; | ||
} | ||
} | ||
} | ||
|
||
.c--upload:enabled { | ||
.content { | ||
.preview { | ||
@apply hover:border-palette-fg-low; | ||
} | ||
|
||
.action { | ||
@apply hover:enabled:border-palette-fg-low hover:disabled:cursor-not-allowed; | ||
} | ||
} | ||
} | ||
|
||
.c--upload:disabled { | ||
.content { | ||
@apply cursor-not-allowed text-palette-bg-low border-palette-bg-low; | ||
|
||
.preview { | ||
@apply cursor-not-allowed; | ||
} | ||
|
||
.action { | ||
@apply cursor-not-allowed; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.