Implement SortModal and TransformModal (WIP)
This commit is contained in:
parent
8256bd637c
commit
37fcdf85e2
|
@ -735,6 +735,12 @@
|
|||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"is-plain-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-reference": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
||||
|
@ -745,9 +751,9 @@
|
|||
}
|
||||
},
|
||||
"is-regex": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
|
||||
"integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
|
||||
"integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-symbols": "^1.0.1"
|
||||
|
@ -802,6 +808,11 @@
|
|||
"iterate-iterator": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k="
|
||||
},
|
||||
"jest-worker": {
|
||||
"version": "26.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz",
|
||||
|
@ -889,12 +900,6 @@
|
|||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
|
||||
|
@ -952,9 +957,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"mocha": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz",
|
||||
"integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==",
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz",
|
||||
"integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-colors": "4.1.1",
|
||||
|
@ -973,7 +978,7 @@
|
|||
"ms": "2.1.2",
|
||||
"object.assign": "4.1.0",
|
||||
"promise.allsettled": "1.0.2",
|
||||
"serialize-javascript": "3.0.0",
|
||||
"serialize-javascript": "4.0.0",
|
||||
"strip-json-comments": "3.0.1",
|
||||
"supports-color": "7.1.0",
|
||||
"which": "2.0.2",
|
||||
|
@ -981,7 +986,7 @@
|
|||
"workerpool": "6.0.0",
|
||||
"yargs": "13.3.2",
|
||||
"yargs-parser": "13.1.2",
|
||||
"yargs-unparser": "1.6.0"
|
||||
"yargs-unparser": "1.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chokidar": {
|
||||
|
@ -1010,10 +1015,13 @@
|
|||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz",
|
||||
"integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==",
|
||||
"dev": true
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1447,6 +1455,11 @@
|
|||
"strip-indent": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"svelte-select": {
|
||||
"version": "3.11.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-3.11.1.tgz",
|
||||
"integrity": "sha512-fva5VRmZT/MnqTMXOlwkEnX+ultdTKdMyguTOngzt77NsXjvPxA7+/M8cUlxyQkAJEqI1RdxCq2RRAAxaJxNBg=="
|
||||
},
|
||||
"svelte-simple-modal": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-simple-modal/-/svelte-simple-modal-0.6.0.tgz",
|
||||
|
@ -1673,14 +1686,107 @@
|
|||
}
|
||||
},
|
||||
"yargs-unparser": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
|
||||
"integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
|
||||
"integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.3.1",
|
||||
"decamelize": "^1.2.0",
|
||||
"flat": "^4.1.0",
|
||||
"lodash": "^4.17.15",
|
||||
"yargs": "^13.3.0"
|
||||
"is-plain-obj": "^1.1.0",
|
||||
"yargs": "^14.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
|
||||
"integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^5.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^3.0.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^3.0.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^15.0.1"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
|
||||
"integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
"ace-builds": "1.4.12",
|
||||
"ajv": "6.12.3",
|
||||
"classnames": "2.2.6",
|
||||
"javascript-natural-sort": "0.7.1",
|
||||
"lodash-es": "4.17.15",
|
||||
"svelte-awesome": "2.3.0",
|
||||
"svelte-select": "3.11.1",
|
||||
"svelte-simple-modal": "0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -31,7 +33,7 @@
|
|||
"@rollup/plugin-node-resolve": "8.4.0",
|
||||
"btoa": "1.2.1",
|
||||
"mkdirp": "1.0.4",
|
||||
"mocha": "8.0.1",
|
||||
"mocha": "8.1.1",
|
||||
"rollup": "2.23.0",
|
||||
"rollup-plugin-livereload": "1.3.0",
|
||||
"rollup-plugin-svelte": "5.2.3",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import Modal from 'svelte-simple-modal'
|
||||
import TreeMode from './treemode/TreeMode.svelte'
|
||||
|
@ -6,6 +8,43 @@
|
|||
|
||||
export let config = {}
|
||||
|
||||
let ref
|
||||
|
||||
export function set (json) {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
ref.set(json)
|
||||
}
|
||||
|
||||
export function get () {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
return ref.get()
|
||||
}
|
||||
|
||||
export function expand (callback) {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
return ref.expand(callback)
|
||||
}
|
||||
|
||||
export function collapse (callback) {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
return ref.collapse(callback)
|
||||
}
|
||||
|
||||
export function setValidator (newValidate) {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
ref.setValidator(newValidate)
|
||||
}
|
||||
|
||||
export function getValidator () {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
return ref.getValidator()
|
||||
}
|
||||
|
||||
export function patch(operations, newSelection) {
|
||||
// TODO: check if the method exists for this mode, if not, throw a clear error
|
||||
return ref.patch(operations, newSelection)
|
||||
}
|
||||
|
||||
function getRestConfig (config) {
|
||||
let { mode, ...restConfig } = config
|
||||
return restConfig
|
||||
|
@ -13,5 +52,10 @@
|
|||
</script>
|
||||
|
||||
<Modal>
|
||||
<svelte:component this={config.mode || DefaultMode} {...getRestConfig(config)} />
|
||||
<!-- TODO: pass the config options explicitly here? -->
|
||||
<svelte:component
|
||||
this={config.mode || DefaultMode}
|
||||
bind:this={ref}
|
||||
{...getRestConfig(config)}
|
||||
/>
|
||||
</Modal>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
@import '../../styles.scss';
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
background: $theme-color;
|
||||
color: $white;
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
padding: $input-padding;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
button.close {
|
||||
min-width: 32px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
export let title = 'Modal'
|
||||
|
||||
const {close} = getContext('simple-modal')
|
||||
</script>
|
||||
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
{title}
|
||||
</div>
|
||||
<button class="close" on:click={close}>
|
||||
<Icon data={faTimes} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style src="./Header.scss"></style>
|
|
@ -0,0 +1,13 @@
|
|||
@import '../../styles.scss';
|
||||
|
||||
.contents {
|
||||
padding: 20px;
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
padding-top: $padding;
|
||||
}
|
||||
}
|
|
@ -1 +1,17 @@
|
|||
@import '../../styles.scss';
|
||||
@import './Modal.scss';
|
||||
|
||||
.sort-modal {
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: none;
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
font-weight: normal;
|
||||
padding-bottom: $padding;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,138 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import naturalSort from 'javascript-natural-sort'
|
||||
import Select from 'svelte-select'
|
||||
import Header from './Header.svelte'
|
||||
import { getNestedPaths } from '../../utils/arrayUtils.js'
|
||||
import { getIn, setIn } from '../../utils/immutabilityHelpers.js'
|
||||
|
||||
export let json
|
||||
export let path
|
||||
export let onSort
|
||||
|
||||
const {close} = getContext('simple-modal')
|
||||
|
||||
$: root = getIn(json, path)
|
||||
$: isArray = Array.isArray(root)
|
||||
$: paths = isArray ? getNestedPaths(root) : undefined
|
||||
$: properties = paths?.map(pathToOption)
|
||||
|
||||
const asc = {
|
||||
value: 1,
|
||||
label: 'asc'
|
||||
}
|
||||
const desc = {
|
||||
value: -1,
|
||||
label: 'desc'
|
||||
}
|
||||
const directions = [ asc, desc ]
|
||||
|
||||
let selectedProperty = undefined
|
||||
let selectedDirection = asc
|
||||
|
||||
function pathToOption (path) {
|
||||
return {
|
||||
value: path,
|
||||
label: path.join('.')
|
||||
}
|
||||
}
|
||||
|
||||
function handleSort () {
|
||||
if (!selectedProperty || !selectedDirection) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: create a sortBy which returns a JSONPatch document
|
||||
|
||||
// TODO: sort object keys when root is an object
|
||||
|
||||
const property = selectedProperty.value
|
||||
const direction = selectedDirection.value
|
||||
|
||||
function comparator (a, b) {
|
||||
const valueA = getIn(a, property)
|
||||
const valueB = getIn(b, property)
|
||||
|
||||
if (valueA === undefined) {
|
||||
return direction
|
||||
}
|
||||
if (valueB === undefined) {
|
||||
return -direction
|
||||
}
|
||||
|
||||
if (typeof valueA !== 'string' && typeof valueB !== 'string') {
|
||||
// both values are a number, boolean, or null -> use simple, fast sorting
|
||||
return valueA > valueB
|
||||
? direction
|
||||
: valueA < valueB
|
||||
? -direction
|
||||
: 0
|
||||
}
|
||||
|
||||
return direction * naturalSort(valueA, valueB)
|
||||
}
|
||||
|
||||
// TODO: use lodash orderBy, split comparator and direction?
|
||||
const sorted = root.slice()
|
||||
sorted.sort(comparator)
|
||||
|
||||
onSort(setIn(json, path, sorted))
|
||||
close()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
Sort Modal...
|
||||
<div class="sort-modal">
|
||||
<Header title={isArray ? 'Sort array items' : 'Sort object keys'} />
|
||||
|
||||
<div class="contents">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col width="25%">
|
||||
<col width="75%">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{#if path.length > 0}
|
||||
<tr>
|
||||
<th>Path</th>
|
||||
<td>{path.join('.')}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if isArray}
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<td>
|
||||
<Select
|
||||
items={properties}
|
||||
bind:selectedValue={selectedProperty}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
<tr>
|
||||
<th>Direction</th>
|
||||
<td>
|
||||
<Select
|
||||
items={directions}
|
||||
bind:selectedValue={selectedDirection}
|
||||
isClearable={false}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="actions">
|
||||
<button
|
||||
class="primary"
|
||||
on:click={handleSort}
|
||||
disabled={isArray ? !selectedProperty : false}
|
||||
>
|
||||
Sort
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style src="./SortModal.scss"></style>
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
@import '../../styles.scss';
|
||||
@import './Modal.scss';
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import Header from './Header.svelte'
|
||||
|
||||
|
||||
const {close} = getContext('simple-modal')
|
||||
</script>
|
||||
|
||||
<div>
|
||||
Transform Modal...
|
||||
<div class="transform-modal">
|
||||
<Header title='Transform' />
|
||||
<div class="contents">
|
||||
TODO...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style src="./TransformModal.scss"></style>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import { debounce, isEqual } from 'lodash-es'
|
||||
import { rename } from '../../logic/operations.js'
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
// FIXME: should utilize the generic styling in styles.scss
|
||||
.button {
|
||||
width: $menu-button-size;
|
||||
height: $menu-button-size;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import Icon from 'svelte-awesome'
|
||||
import { faCut, faClone, faCopy, faPaste, faSearch, faUndo, faRedo, faPlus, faTimes, faFilter, faSortAmountDownAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash-es'
|
||||
import Icon from 'svelte-awesome'
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<svelte:options immutable={true} />
|
||||
|
||||
<script>
|
||||
import { getContext, tick } from 'svelte'
|
||||
import {
|
||||
|
@ -260,7 +262,20 @@
|
|||
}
|
||||
|
||||
function handleSort () {
|
||||
open(SortModal, {}, SIMPLE_MODAL_OPTIONS)
|
||||
open(SortModal, {
|
||||
json: doc,
|
||||
path: [], // FIXME: based on selection
|
||||
onSort: sortedDoc => {
|
||||
console.log('onSort', sortedDoc)
|
||||
doc = sortedDoc
|
||||
}
|
||||
}, {
|
||||
...SIMPLE_MODAL_OPTIONS,
|
||||
styleWindow: {
|
||||
...SIMPLE_MODAL_OPTIONS.styleWindow,
|
||||
width: '400px'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleTransform () {
|
||||
|
|
|
@ -21,5 +21,9 @@ export const SIMPLE_MODAL_OPTIONS = {
|
|||
},
|
||||
styleWindow: {
|
||||
borderRadius: '2px'
|
||||
},
|
||||
styleContent: {
|
||||
padding: '0px',
|
||||
overflow: 'visible'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ export default function jsoneditor (config) {
|
|||
})
|
||||
}
|
||||
|
||||
// modes
|
||||
export const TreeMode = _TreeMode
|
||||
|
||||
// plugins
|
||||
export { createAjvValidator } from './plugins/createAjvValidator.mjs'
|
||||
|
|
|
@ -35,6 +35,7 @@ $input-padding: 5px;
|
|||
$search-box-offset: 10px;
|
||||
$border-radius: 3px;
|
||||
|
||||
$padding: 10px;
|
||||
$menu-padding: 5px;
|
||||
$bottom-height: 5px;
|
||||
|
||||
|
@ -49,3 +50,18 @@ button {
|
|||
font-size: $font-size;
|
||||
padding: $menu-padding;
|
||||
}
|
||||
|
||||
button.primary {
|
||||
background: $theme-color;
|
||||
color: $white;
|
||||
padding: $padding 2 * $padding;
|
||||
border-radius: $border-radius;
|
||||
|
||||
&:hover {
|
||||
background: lighten($theme-color, 7%);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: $gray;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,9 @@
|
|||
import { isObject } from './typeUtils.js'
|
||||
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
|
||||
|
||||
const MAX_ITEM_PATHS_COLLECTION = 10000
|
||||
const EMPTY_ARRAY = []
|
||||
|
||||
/**
|
||||
* Comparator to sort an array in ascending order
|
||||
*
|
||||
|
@ -60,3 +66,41 @@ export function compareArrays(a, b) {
|
|||
|
||||
return a.length - b.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the paths of all nested properties in the items of an array
|
||||
* @param {JSON} json
|
||||
* @param {boolean} [includeObjects=false] If true, object and array paths are returned as well
|
||||
* @return {Path[]}
|
||||
*/
|
||||
export function getNestedPaths (array, includeObjects = false) {
|
||||
const pathsMap = {}
|
||||
|
||||
if (!Array.isArray(array)) {
|
||||
throw new TypeError('Array expected')
|
||||
}
|
||||
|
||||
function recurseNestedPaths (obj, path) {
|
||||
const isValue = !Array.isArray(obj) && !isObject(obj)
|
||||
|
||||
if (isValue || includeObjects) {
|
||||
pathsMap[compileJSONPointer(path)] = true
|
||||
}
|
||||
|
||||
if (isObject(obj)) {
|
||||
Object.keys(obj).forEach(key => {
|
||||
recurseNestedPaths(obj[key], path.concat(key))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const max = Math.min(array.length, MAX_ITEM_PATHS_COLLECTION)
|
||||
for (let i = 0; i < max; i++) {
|
||||
const item = array[i]
|
||||
recurseNestedPaths(item, EMPTY_ARRAY)
|
||||
}
|
||||
|
||||
const pathsArray = Object.keys(pathsMap).sort()
|
||||
|
||||
return pathsArray.map(parseJSONPointer)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import assert from "assert"
|
||||
import { compareArrays } from './arrayUtils.js'
|
||||
import { compareArrays, getNestedPaths } from './arrayUtils.js'
|
||||
|
||||
describe('arrayUtils', () => {
|
||||
it('compareArrays', () => {
|
||||
|
@ -27,4 +27,64 @@ describe('arrayUtils', () => {
|
|||
['b', 'c']
|
||||
])
|
||||
})
|
||||
|
||||
describe('getNestedPaths', () => {
|
||||
it('should extract all nested paths of an array containing objects', () => {
|
||||
const json = [
|
||||
{ name: 'A', location: { latitude: 1, longitude: 2 } },
|
||||
{ name: 'B', location: { latitude: 1, longitude: 2 } },
|
||||
{ name: 'C', timestamp: 0 }
|
||||
]
|
||||
|
||||
assert.deepStrictEqual(getNestedPaths(json), [
|
||||
['location', 'latitude'],
|
||||
['location', 'longitude'],
|
||||
['name'],
|
||||
['timestamp']
|
||||
])
|
||||
})
|
||||
|
||||
it('should extract a path containing an empty key', () => {
|
||||
const json = [
|
||||
{ '': 'empty' }
|
||||
]
|
||||
|
||||
assert.deepStrictEqual(getNestedPaths(json), [
|
||||
['']
|
||||
])
|
||||
})
|
||||
|
||||
it('should extract all nested paths of an array containing objects, including objects', () => {
|
||||
const json = [
|
||||
{ name: 'A', location: { latitude: 1, longitude: 2 } },
|
||||
{ name: 'B', location: { latitude: 1, longitude: 2 } },
|
||||
{ name: 'C', timestamp: 0 }
|
||||
]
|
||||
|
||||
console.log('paths', getNestedPaths(json, true))
|
||||
|
||||
assert.deepStrictEqual(getNestedPaths(json, true), [
|
||||
[],
|
||||
['location'],
|
||||
['location', 'latitude'],
|
||||
['location', 'longitude'],
|
||||
['name'],
|
||||
['timestamp']
|
||||
])
|
||||
})
|
||||
|
||||
it('should extract all nested paths of an array containing values', () => {
|
||||
const json = [1, 2, 3]
|
||||
|
||||
assert.deepStrictEqual(getNestedPaths(json), [
|
||||
[]
|
||||
])
|
||||
})
|
||||
|
||||
it('should throw an error when not passing an array', () => {
|
||||
assert.throws(() => getNestedPaths({ a: 2, b: { c: 3 } }), /TypeError: Array expected/)
|
||||
assert.throws(() => getNestedPaths('foo'), /TypeError: Array expected/)
|
||||
assert.throws(() => getNestedPaths(123), /TypeError: Array expected/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue