add natsort
This commit is contained in:
parent
cb4766cc17
commit
2a9328665a
@ -4,6 +4,7 @@ import { extname, join } from "path/posix.ts";
|
||||
import { ComponentChild } from "preact";
|
||||
import UpList from "./UpList.tsx";
|
||||
import { extToIcon } from "../src/media.ts";
|
||||
import natsort from "../src/natsort.ts";
|
||||
import { encodePath } from "../util/util.ts";
|
||||
|
||||
function ListItem(props: {
|
||||
@ -34,10 +35,15 @@ interface DirListProps {
|
||||
path: string;
|
||||
files: EntryInfo[];
|
||||
}
|
||||
const natsortCompare = natsort();
|
||||
|
||||
export function DirList(props: DirListProps) {
|
||||
const data = props;
|
||||
const [files, setFiles] = useState(data.files);
|
||||
const [files, setFiles] = useState(
|
||||
data.files.toSorted(
|
||||
(a,b)=> natsortCompare(a.name,b.name)
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -95,7 +101,7 @@ export function DirList(props: DirListProps) {
|
||||
const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
|
||||
.sort(
|
||||
([a, ai], [b, bi]) => {
|
||||
const ret = a.name.localeCompare(b.name);
|
||||
const ret = natsortCompare(a.name, b.name);
|
||||
if (ret === 0) {
|
||||
return ai - bi;
|
||||
} else {
|
||||
@ -107,4 +113,6 @@ export function DirList(props: DirListProps) {
|
||||
}
|
||||
}
|
||||
|
||||
export default DirList;
|
||||
export default function DirListIsland(props: DirListProps){
|
||||
return <DirList {...props}/>
|
||||
}
|
||||
|
174
src/natsort.ts
Normal file
174
src/natsort.ts
Normal file
@ -0,0 +1,174 @@
|
||||
/**
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 W.Y.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
export type OptionsType = {
|
||||
/*
|
||||
* Desc sorting.
|
||||
*/
|
||||
desc?: boolean,
|
||||
/*
|
||||
* Case-Insensitive sorting.
|
||||
*/
|
||||
insensitive?: boolean,
|
||||
}
|
||||
|
||||
export default function natsort(options: OptionsType = {}) {
|
||||
|
||||
const ore = /^0/
|
||||
const sre = /\s+/g
|
||||
const tre = /^\s+|\s+$/g
|
||||
// unicode
|
||||
const ure = /[^\x00-\x80]/
|
||||
// hex
|
||||
const hre = /^0x[0-9a-f]+$/i
|
||||
// numeric
|
||||
const nre = /(0x[\da-fA-F]+|(^[\+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|\d+)/g
|
||||
// datetime
|
||||
const dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/ // tslint:disable-line
|
||||
const toLowerCase = String.prototype.toLocaleLowerCase || String.prototype.toLowerCase
|
||||
|
||||
const GREATER = options.desc ? -1 : 1
|
||||
const SMALLER = -GREATER
|
||||
const normalize = options.insensitive
|
||||
? (s: string | number) => toLowerCase.call(`${s}`).replace(tre, '')
|
||||
: (s: string | number) => (`${s}`).replace(tre, '')
|
||||
|
||||
function tokenize(s: string): string[] {
|
||||
return s.replace(nre, '\0$1\0')
|
||||
.replace(/\0$/, '')
|
||||
.replace(/^\0/, '')
|
||||
.split('\0')
|
||||
}
|
||||
|
||||
function parse(s: string, l: number) {
|
||||
// normalize spaces; find floats not starting with '0',
|
||||
// string or 0 if not defined (Clint Priest)
|
||||
return (!s.match(ore) || l === 1)
|
||||
&& parseFloat(s)
|
||||
|| s.replace(sre, ' ').replace(tre, '')
|
||||
|| 0
|
||||
}
|
||||
|
||||
return function (
|
||||
a: string | number,
|
||||
b: string | number,
|
||||
): number {
|
||||
|
||||
// trim pre-post whitespace
|
||||
const aa = normalize(a)
|
||||
const bb = normalize(b)
|
||||
|
||||
// return immediately if at least one of the values is empty.
|
||||
// empty string < any others
|
||||
if (!aa && !bb) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (!aa && bb) {
|
||||
return SMALLER
|
||||
}
|
||||
|
||||
if (aa && !bb) {
|
||||
return GREATER
|
||||
}
|
||||
|
||||
// tokenize: split numeric strings and default strings
|
||||
const aArr = tokenize(aa)
|
||||
const bArr = tokenize(bb)
|
||||
|
||||
// hex or date detection
|
||||
const aHex = aa.match(hre)
|
||||
const bHex = bb.match(hre)
|
||||
const av = (aHex && bHex) ? parseInt(aHex[0], 16) : (aArr.length !== 1 && Date.parse(aa))
|
||||
const bv = (aHex && bHex)
|
||||
? parseInt(bHex[0], 16)
|
||||
: av && bb.match(dre) && Date.parse(bb) || null
|
||||
|
||||
// try and sort Hex codes or Dates
|
||||
if (bv) {
|
||||
if (av === bv) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (av < bv) {
|
||||
return SMALLER
|
||||
}
|
||||
|
||||
if (av > bv) {
|
||||
return GREATER
|
||||
}
|
||||
}
|
||||
|
||||
const al = aArr.length
|
||||
const bl = bArr.length
|
||||
|
||||
// handle numeric strings and default strings
|
||||
for (let i = 0, l = Math.max(al, bl); i < l; i += 1) {
|
||||
|
||||
const af = parse(aArr[i] || '', al)
|
||||
const bf = parse(bArr[i] || '', bl)
|
||||
|
||||
// handle numeric vs string comparison.
|
||||
// numeric < string
|
||||
if (isNaN(af as number) !== isNaN(bf as number)) {
|
||||
return isNaN(af as number) ? GREATER : SMALLER
|
||||
}
|
||||
|
||||
// if unicode use locale comparison
|
||||
if (ure.test((af as string) + (bf as string)) && (af as string).localeCompare) {
|
||||
const comp = (af as string).localeCompare(bf as string)
|
||||
|
||||
if (comp > 0) {
|
||||
return GREATER
|
||||
}
|
||||
|
||||
if (comp < 0) {
|
||||
return SMALLER
|
||||
}
|
||||
|
||||
if (i === l - 1) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
if (af < bf) {
|
||||
return SMALLER
|
||||
}
|
||||
|
||||
if (af > bf) {
|
||||
return GREATER
|
||||
}
|
||||
|
||||
if (`${af}` < `${bf}`) {
|
||||
return SMALLER
|
||||
}
|
||||
|
||||
if (`${af}` > `${bf}`) {
|
||||
return GREATER
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user