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 { ComponentChild } from "preact";
|
||||||
import UpList from "./UpList.tsx";
|
import UpList from "./UpList.tsx";
|
||||||
import { extToIcon } from "../src/media.ts";
|
import { extToIcon } from "../src/media.ts";
|
||||||
|
import natsort from "../src/natsort.ts";
|
||||||
import { encodePath } from "../util/util.ts";
|
import { encodePath } from "../util/util.ts";
|
||||||
|
|
||||||
function ListItem(props: {
|
function ListItem(props: {
|
||||||
@ -34,10 +35,15 @@ interface DirListProps {
|
|||||||
path: string;
|
path: string;
|
||||||
files: EntryInfo[];
|
files: EntryInfo[];
|
||||||
}
|
}
|
||||||
|
const natsortCompare = natsort();
|
||||||
|
|
||||||
export function DirList(props: DirListProps) {
|
export function DirList(props: DirListProps) {
|
||||||
const data = props;
|
const data = props;
|
||||||
const [files, setFiles] = useState(data.files);
|
const [files, setFiles] = useState(
|
||||||
|
data.files.toSorted(
|
||||||
|
(a,b)=> natsortCompare(a.name,b.name)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -95,7 +101,7 @@ export function DirList(props: DirListProps) {
|
|||||||
const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
|
const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
|
||||||
.sort(
|
.sort(
|
||||||
([a, ai], [b, bi]) => {
|
([a, ai], [b, bi]) => {
|
||||||
const ret = a.name.localeCompare(b.name);
|
const ret = natsortCompare(a.name, b.name);
|
||||||
if (ret === 0) {
|
if (ret === 0) {
|
||||||
return ai - bi;
|
return ai - bi;
|
||||||
} else {
|
} 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