ui fix
This commit is contained in:
parent
eb06476f38
commit
a4a11b2638
29
package-lock.json
generated
29
package-lock.json
generated
@ -66,21 +66,16 @@
|
|||||||
"svelte-check": "^3.6.9",
|
"svelte-check": "^3.6.9",
|
||||||
"svelte-echarts": "^1.0.0-rc3",
|
"svelte-echarts": "^1.0.0-rc3",
|
||||||
"svelte-french-toast": "^1.2.0",
|
"svelte-french-toast": "^1.2.0",
|
||||||
"svelte-intersection-observer": "^1.0.0",
|
|
||||||
"svelte-intersection-observer-action": "^0.0.5",
|
|
||||||
"svelte-inview": "^4.0.2",
|
"svelte-inview": "^4.0.2",
|
||||||
"svelte-lazy": "^1.2.11",
|
"svelte-lazy": "^1.2.11",
|
||||||
"svelte-lightweight-charts": "^2.2.0",
|
"svelte-lightweight-charts": "^2.2.0",
|
||||||
"svelte-loading-spinners": "^0.3.6",
|
"svelte-loading-spinners": "^0.3.6",
|
||||||
"svelte-preprocess": "^5.1.4",
|
"svelte-preprocess": "^5.1.4",
|
||||||
"svelte-progress-bar": "^3.0.2",
|
|
||||||
"svelte-sonner": "^0.3.27",
|
"svelte-sonner": "^0.3.27",
|
||||||
"svelte-tags-input": "^6.0.1",
|
|
||||||
"svelte-tiny-virtual-list": "^2.1.2",
|
"svelte-tiny-virtual-list": "^2.1.2",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwind-variants": "^0.2.1",
|
"tailwind-variants": "^0.2.1",
|
||||||
"tailwindcss": "^4.0.9",
|
"tailwindcss": "^4.0.9",
|
||||||
"tslib": "^2.7.0",
|
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
@ -8692,18 +8687,6 @@
|
|||||||
"svelte": "^3.19.0 || ^4.0.0"
|
"svelte": "^3.19.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-intersection-observer": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-intersection-observer/-/svelte-intersection-observer-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-AoxSog8fSt9sSN8ajMYM1I48ndq+rmPlaZSkJFCRbZ7I3KO8IPX6pl5mewWxdYL1JDI06hHu/7epn3h5tlJV9w==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/svelte-intersection-observer-action": {
|
|
||||||
"version": "0.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-intersection-observer-action/-/svelte-intersection-observer-action-0.0.5.tgz",
|
|
||||||
"integrity": "sha512-d5WVlE3Dpxx554cOYBm9BkIfba0+/r1O1yKuekQH/ScXW78mNB+R7A/LSfmGTFBNHvqjPZ9Vehfx1iIJti1saw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/svelte-inview": {
|
"node_modules/svelte-inview": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-inview/-/svelte-inview-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-inview/-/svelte-inview-4.0.2.tgz",
|
||||||
@ -8817,12 +8800,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-progress-bar": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-progress-bar/-/svelte-progress-bar-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-OpHgSg7ebvCtzA1mnUzOSftG7qcJ6llbopGuLCQ3Usz8RnFMm5S4DcclaaSDHQ33NJ8s+SrN/sEVuKZIDF7asw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/svelte-sonner": {
|
"node_modules/svelte-sonner": {
|
||||||
"version": "0.3.27",
|
"version": "0.3.27",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-0.3.27.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-0.3.27.tgz",
|
||||||
@ -8832,12 +8809,6 @@
|
|||||||
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1"
|
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-tags-input": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-tags-input/-/svelte-tags-input-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-3X5qomFSXe6E8H7Lq0oce7096tr6u06pWvTNgNoeNVIXCrRLxRYOk4Ujkte7z5WaAit47EUsZZP1TSJ2HR9ixA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/svelte-tiny-virtual-list": {
|
"node_modules/svelte-tiny-virtual-list": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-tiny-virtual-list/-/svelte-tiny-virtual-list-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-tiny-virtual-list/-/svelte-tiny-virtual-list-2.1.2.tgz",
|
||||||
|
|||||||
@ -66,21 +66,16 @@
|
|||||||
"svelte-check": "^3.6.9",
|
"svelte-check": "^3.6.9",
|
||||||
"svelte-echarts": "^1.0.0-rc3",
|
"svelte-echarts": "^1.0.0-rc3",
|
||||||
"svelte-french-toast": "^1.2.0",
|
"svelte-french-toast": "^1.2.0",
|
||||||
"svelte-intersection-observer": "^1.0.0",
|
|
||||||
"svelte-intersection-observer-action": "^0.0.5",
|
|
||||||
"svelte-inview": "^4.0.2",
|
"svelte-inview": "^4.0.2",
|
||||||
"svelte-lazy": "^1.2.11",
|
"svelte-lazy": "^1.2.11",
|
||||||
"svelte-lightweight-charts": "^2.2.0",
|
"svelte-lightweight-charts": "^2.2.0",
|
||||||
"svelte-loading-spinners": "^0.3.6",
|
"svelte-loading-spinners": "^0.3.6",
|
||||||
"svelte-preprocess": "^5.1.4",
|
"svelte-preprocess": "^5.1.4",
|
||||||
"svelte-progress-bar": "^3.0.2",
|
|
||||||
"svelte-sonner": "^0.3.27",
|
"svelte-sonner": "^0.3.27",
|
||||||
"svelte-tags-input": "^6.0.1",
|
|
||||||
"svelte-tiny-virtual-list": "^2.1.2",
|
"svelte-tiny-virtual-list": "^2.1.2",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwind-variants": "^0.2.1",
|
"tailwind-variants": "^0.2.1",
|
||||||
"tailwindcss": "^4.0.9",
|
"tailwindcss": "^4.0.9",
|
||||||
"tslib": "^2.7.0",
|
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
|
|||||||
@ -1,199 +0,0 @@
|
|||||||
<!--
|
|
||||||
@component
|
|
||||||
Generates an HTML circle pack chart using [d3-hierarchy](https://github.com/d3/d3-hierarchy).
|
|
||||||
-->
|
|
||||||
<script>
|
|
||||||
import { stratify, pack, hierarchy } from "d3-hierarchy";
|
|
||||||
import { getContext } from "svelte";
|
|
||||||
import { format } from "d3-format";
|
|
||||||
import { screenWidth } from "$lib/store";
|
|
||||||
|
|
||||||
const { width, height, data } = getContext("LayerCake");
|
|
||||||
|
|
||||||
/** @type {String} [idKey='id'] - The key on each object where the id value lives. */
|
|
||||||
export let idKey = "id";
|
|
||||||
|
|
||||||
/** @type {String} [parentKey] - Set this if you want to define one parent circle. This will give you a [nested](https://layercake.graphics/example/CirclePackNested) graphic versus a [grouping of circles](https://layercake.graphics/example/CirclePack). */
|
|
||||||
export let parentKey = undefined;
|
|
||||||
|
|
||||||
/** @type {String} [valueKey='value'] - The key on each object where the data value lives. */
|
|
||||||
export let valueKey = "value";
|
|
||||||
|
|
||||||
/** @type {Function} [labelVisibilityThreshold=r => r > 25] - By default, only show the text inside a circle if its radius exceeds a certain size. Provide your own function for different behavior. */
|
|
||||||
export let labelVisibilityThreshold = (r) => r > 25;
|
|
||||||
|
|
||||||
/** @type {String} [stroke='#999'] - The circle's stroke color. */
|
|
||||||
export let stroke = "#000";
|
|
||||||
|
|
||||||
/** @type {String} [textColor='#333'] - The label text color. */
|
|
||||||
export let textColor = "#000";
|
|
||||||
|
|
||||||
/** @type {String} [textStroke='#000'] - The label text's stroke color. */
|
|
||||||
export let textStroke = "#000";
|
|
||||||
|
|
||||||
/** @type {Number} [textStrokeWidth=0] - The label text's stroke width, in pixels. */
|
|
||||||
export let textStrokeWidth = 0;
|
|
||||||
|
|
||||||
/** @type {Function} [sortBy=(a, b) => b.value - a.value] - The order in which circle's are drawn. Sorting on the `depth` key is also a popular choice. */
|
|
||||||
export let sortBy = (a, b) => b.value - a.value; // 'depth' is also a popular choice
|
|
||||||
|
|
||||||
/** @type {Number} [spacing=0] - Whitespace padding between each circle, in pixels. */
|
|
||||||
export let spacing = 0;
|
|
||||||
|
|
||||||
/* --------------------------------------------
|
|
||||||
* This component will automatically group your data
|
|
||||||
* into one group if no `parentKey` was passed in.
|
|
||||||
* Stash $data here so we can add our own parent
|
|
||||||
* if there's no `parentKey`
|
|
||||||
*/
|
|
||||||
let parent = {};
|
|
||||||
$: dataset = $data;
|
|
||||||
|
|
||||||
$: if (parentKey === undefined) {
|
|
||||||
parent = { [idKey]: "all" };
|
|
||||||
dataset = [...dataset, parent];
|
|
||||||
}
|
|
||||||
|
|
||||||
$: stratifier = stratify()
|
|
||||||
.id((d) => d[idKey])
|
|
||||||
.parentId((d) => {
|
|
||||||
if (d[idKey] === parent[idKey]) return "";
|
|
||||||
return d[parentKey] || parent[idKey];
|
|
||||||
});
|
|
||||||
|
|
||||||
$: packer = pack().size([$width, $height]).padding(spacing);
|
|
||||||
|
|
||||||
$: stratified = stratifier(dataset);
|
|
||||||
|
|
||||||
$: root = hierarchy(stratified)
|
|
||||||
.sum((d, i) => {
|
|
||||||
return d.data[valueKey] || 1;
|
|
||||||
})
|
|
||||||
.sort(sortBy);
|
|
||||||
|
|
||||||
$: packed = packer(root);
|
|
||||||
|
|
||||||
$: descendants = packed.descendants();
|
|
||||||
|
|
||||||
$: ballSize = $screenWidth < 1024 ? 2 : 3;
|
|
||||||
|
|
||||||
const titleCase = (d) => d.replace(/^\w/, (w) => w.toUpperCase());
|
|
||||||
const commas = format(",");
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="circle-pack" data-has-parent-key={parentKey !== undefined}>
|
|
||||||
{#each descendants as d, index}
|
|
||||||
<div
|
|
||||||
class="circle-group"
|
|
||||||
data-id={d.data.id}
|
|
||||||
data-visible={labelVisibilityThreshold(d.r)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="circle"
|
|
||||||
style="left:{d.x}px;top:{d.y}px;width:{d.r * ballSize}px;height:{d.r *
|
|
||||||
ballSize}px;background-color:{index === 1 && d.data.id === 'puts'
|
|
||||||
? '#FF2F1F'
|
|
||||||
: index === 1 && d.data.id === 'calls'
|
|
||||||
? '#00FC50'
|
|
||||||
: '#1E1E1E'}; border: 0px solid #000;"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="text-group"
|
|
||||||
style="
|
|
||||||
color:{textColor};
|
|
||||||
text-shadow:
|
|
||||||
-{textStrokeWidth}px -{textStrokeWidth}px 0 {textStroke},
|
|
||||||
{textStrokeWidth}px -{textStrokeWidth}px 0 {textStroke},
|
|
||||||
-{textStrokeWidth}px {textStrokeWidth}px 0 {textStroke},
|
|
||||||
{textStrokeWidth}px {textStrokeWidth}px 0 {textStroke};
|
|
||||||
left:{d.x}px;
|
|
||||||
top:{d.y - (labelVisibilityThreshold(d.r) ? 0 : d.r + 4)}px;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-center m-auto">
|
|
||||||
{#if d.data.data[valueKey]}
|
|
||||||
<div
|
|
||||||
class="{index === 1
|
|
||||||
? 'text-xl font-semibold text-black'
|
|
||||||
: 'text-[1rem] font-semibold text-[#6B6C70]'} text-center"
|
|
||||||
>
|
|
||||||
{commas(d.data.data[valueKey])}%
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="{index === 1
|
|
||||||
? 'text-xl font-semibold text-black'
|
|
||||||
: 'text-sm font-semibold text-[#6B6C70]'} text-center"
|
|
||||||
>
|
|
||||||
{titleCase(d.data.id)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.circle-pack {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.circle,
|
|
||||||
.text-group {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.circle {
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
/* Hide the root node if we want, useful if we are creating our own root */
|
|
||||||
.circle-pack[data-has-parent-key="false"] .circle-group[data-id="all"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* .circle-group:hover {
|
|
||||||
z-index: 9999;
|
|
||||||
} */
|
|
||||||
.circle-group[data-visible="false"] .text-group {
|
|
||||||
display: none;
|
|
||||||
padding: 4px 7px;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
transform: translate(-50%, -100%);
|
|
||||||
top: -4px;
|
|
||||||
}
|
|
||||||
.circle-group[data-visible="false"]:hover .text-group {
|
|
||||||
z-index: 999;
|
|
||||||
display: block !important;
|
|
||||||
/* On hover, set the text color to black and eliminate the shadow */
|
|
||||||
text-shadow: none !important;
|
|
||||||
color: #000 !important;
|
|
||||||
}
|
|
||||||
.circle-group[data-visible="false"]:hover .circle {
|
|
||||||
border-color: #000 !important;
|
|
||||||
}
|
|
||||||
.text-group {
|
|
||||||
width: auto;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
text-align: center;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
white-space: nowrap;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: pointer;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
.text {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 19px;
|
|
||||||
/* text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff; */
|
|
||||||
}
|
|
||||||
.text.value {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
.circle {
|
|
||||||
border-radius: 50%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -3,6 +3,7 @@
|
|||||||
import InfoModal from "$lib/components/InfoModal.svelte";
|
import InfoModal from "$lib/components/InfoModal.svelte";
|
||||||
import { abbreviateNumber, removeCompanyStrings } from "$lib/utils";
|
import { abbreviateNumber, removeCompanyStrings } from "$lib/utils";
|
||||||
import highcharts from "$lib/highcharts.ts";
|
import highcharts from "$lib/highcharts.ts";
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
export let rawData = [];
|
export let rawData = [];
|
||||||
@ -293,7 +294,7 @@
|
|||||||
{#if data?.user?.tier !== "Pro" && i > 0}
|
{#if data?.user?.tier !== "Pro" && i > 0}
|
||||||
<button
|
<button
|
||||||
on:click={() => goto("/pricing")}
|
on:click={() => goto("/pricing")}
|
||||||
class="group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
||||||
>
|
>
|
||||||
<span class="relative text-sm block font-semibold">
|
<span class="relative text-sm block font-semibold">
|
||||||
{item.title}
|
{item.title}
|
||||||
@ -311,7 +312,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
on:click={() => changeTimePeriod(i)}
|
on:click={() => changeTimePeriod(i)}
|
||||||
class="group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
||||||
i
|
i
|
||||||
? 'z-0'
|
? 'z-0'
|
||||||
: ''} "
|
: ''} "
|
||||||
@ -371,7 +372,7 @@
|
|||||||
<td
|
<td
|
||||||
class="text-white text-sm sm:text-[1rem] text-right whitespace-nowrap"
|
class="text-white text-sm sm:text-[1rem] text-right whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{item?.totalVolume}
|
{abbreviateNumber(item?.totalVolume)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td
|
<td
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
let activeIdx = 0;
|
let activeIdx = 0;
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
title: "Annual",
|
title: "Daily",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Quarterly",
|
title: "Quarterly",
|
||||||
@ -31,47 +31,31 @@
|
|||||||
(a, b) => new Date(b?.date) - new Date(a?.date),
|
(a, b) => new Date(b?.date) - new Date(a?.date),
|
||||||
);
|
);
|
||||||
|
|
||||||
tableList = filterByPeriod([...tableList], activeIdx);
|
|
||||||
|
|
||||||
function changeTimePeriod(i) {
|
function changeTimePeriod(i) {
|
||||||
activeIdx = i;
|
activeIdx = i;
|
||||||
tableList = rawData?.sort((a, b) => new Date(b?.date) - new Date(a?.date));
|
tableList = rawData?.sort((a, b) => new Date(b?.date) - new Date(a?.date));
|
||||||
|
|
||||||
tableList = filterByPeriod([...tableList], i);
|
if (activeIdx === 1) {
|
||||||
|
tableList = filterByPeriod([...tableList]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterByPeriod(data, period) {
|
function filterByPeriod(data) {
|
||||||
if (!Array.isArray(data) || data.length === 0) return [];
|
if (!Array.isArray(data) || data.length === 0) return [];
|
||||||
|
|
||||||
if (period === 0) {
|
// Quarterly: one result per year-quarter.
|
||||||
// Annual: one result per year.
|
const seenPeriods = new Set();
|
||||||
const seenYears = new Set();
|
return data.filter((item) => {
|
||||||
return data.filter((item) => {
|
const dt = new Date(item.date);
|
||||||
const dt = new Date(item.date);
|
const year = dt.getFullYear();
|
||||||
const year = dt.getFullYear();
|
const quarter = Math.floor(dt.getMonth() / 3) + 1; // Quarter 1 to 4
|
||||||
if (!seenYears.has(year)) {
|
const key = `${year}-Q${quarter}`;
|
||||||
seenYears.add(year);
|
if (!seenPeriods.has(key)) {
|
||||||
return true;
|
seenPeriods.add(key);
|
||||||
}
|
return true;
|
||||||
return false;
|
}
|
||||||
});
|
return false;
|
||||||
} else if (period === 1) {
|
});
|
||||||
// Quarterly: one result per year-quarter.
|
|
||||||
const seenPeriods = new Set();
|
|
||||||
return data.filter((item) => {
|
|
||||||
const dt = new Date(item.date);
|
|
||||||
const year = dt.getFullYear();
|
|
||||||
const quarter = Math.floor(dt.getMonth() / 3) + 1; // Quarter 1 to 4
|
|
||||||
const key = `${year}-Q${quarter}`;
|
|
||||||
if (!seenPeriods.has(key)) {
|
|
||||||
seenPeriods.add(key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlotOptions() {
|
function getPlotOptions() {
|
||||||
@ -293,7 +277,7 @@
|
|||||||
{#if data?.user?.tier !== "Pro" && i > 0}
|
{#if data?.user?.tier !== "Pro" && i > 0}
|
||||||
<button
|
<button
|
||||||
on:click={() => goto("/pricing")}
|
on:click={() => goto("/pricing")}
|
||||||
class="group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
||||||
>
|
>
|
||||||
<span class="relative text-sm block font-semibold">
|
<span class="relative text-sm block font-semibold">
|
||||||
{item.title}
|
{item.title}
|
||||||
@ -311,7 +295,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
on:click={() => changeTimePeriod(i)}
|
on:click={() => changeTimePeriod(i)}
|
||||||
class="group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
||||||
i
|
i
|
||||||
? 'z-0'
|
? 'z-0'
|
||||||
: ''} "
|
: ''} "
|
||||||
@ -352,9 +336,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{#each tableList as item, index}
|
{#each tableList as item, index}
|
||||||
<!-- row -->
|
<!-- row -->
|
||||||
<tr
|
<tr class="odd:bg-odd">
|
||||||
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border-b border-gray-800"
|
|
||||||
>
|
|
||||||
<td
|
<td
|
||||||
class="text-white font-medium text-sm sm:text-[1rem] whitespace-nowrap"
|
class="text-white font-medium text-sm sm:text-[1rem] whitespace-nowrap"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,119 +0,0 @@
|
|||||||
<!--
|
|
||||||
@component
|
|
||||||
Generates an SVG x-axis. This component is also configured to detect if your x-scale is an ordinal scale. If so, it will place the markers in the middle of the bandwidth.
|
|
||||||
-->
|
|
||||||
<script>
|
|
||||||
import { getContext } from 'svelte';
|
|
||||||
|
|
||||||
const { width, height, xScale, yRange } = getContext('LayerCake');
|
|
||||||
|
|
||||||
/** @type {Boolean} [tickMarks=false] - Show a vertical mark for each tick. */
|
|
||||||
export let tickMarks = false;
|
|
||||||
|
|
||||||
/** @type {Boolean} [gridlines=true] - Show gridlines extending into the chart area. */
|
|
||||||
export let gridlines = true;
|
|
||||||
|
|
||||||
/** @type {Number} [tickMarkLength=6] - The length of the tick mark. */
|
|
||||||
export let tickMarkLength = 6;
|
|
||||||
|
|
||||||
/** @type {Boolean} [baseline=false] – Show a solid line at the bottom. */
|
|
||||||
export let baseline = false;
|
|
||||||
|
|
||||||
/** @type {Boolean} [snapLabels=false] - Instead of centering the text labels on the first and the last items, align them to the edges of the chart. */
|
|
||||||
export let snapLabels = false;
|
|
||||||
|
|
||||||
/** @type {Function} [format=d => d] - A function that passes the current tick value and expects a nicely formatted value in return. */
|
|
||||||
export let format = d => d;
|
|
||||||
|
|
||||||
/** @type {Number|Array|Function} [ticks] - If this is a number, it passes that along to the [d3Scale.ticks](https://github.com/d3/d3-scale) function. If this is an array, hardcodes the ticks to those values. If it's a function, passes along the default tick values and expects an array of tick values in return. If nothing, it uses the default ticks supplied by the D3 function. */
|
|
||||||
export let ticks = undefined;
|
|
||||||
|
|
||||||
/** @type {Number} [tickGutter=0] - The amount of whitespace between the start of the tick and the chart drawing area (the yRange min). */
|
|
||||||
export let tickGutter = 0;
|
|
||||||
|
|
||||||
/** @type {Number} [dx=0] - Any optional value passed to the `dx` attribute on the text label. */
|
|
||||||
export let dx = 0;
|
|
||||||
|
|
||||||
/** @type {Number} [dy=12] - Any optional value passed to the `dy` attribute on the text label. */
|
|
||||||
export let dy = 12;
|
|
||||||
|
|
||||||
function textAnchor(i, sl) {
|
|
||||||
if (sl === true) {
|
|
||||||
if (i === 0) {
|
|
||||||
return 'start';
|
|
||||||
}
|
|
||||||
if (i === tickVals.length - 1) {
|
|
||||||
return 'end';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'middle';
|
|
||||||
}
|
|
||||||
|
|
||||||
$: tickLen = tickMarks === true ? tickMarkLength ?? 6 : 0;
|
|
||||||
|
|
||||||
$: isBandwidth = typeof $xScale.bandwidth === 'function';
|
|
||||||
|
|
||||||
$: tickVals = Array.isArray(ticks)
|
|
||||||
? ticks
|
|
||||||
: isBandwidth
|
|
||||||
? $xScale.domain()
|
|
||||||
: typeof ticks === 'function'
|
|
||||||
? ticks($xScale.ticks())
|
|
||||||
: $xScale.ticks(ticks);
|
|
||||||
|
|
||||||
$: halfBand = isBandwidth ? $xScale.bandwidth() / 2 : 0;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<g class="axis x-axis" class:snapLabels>
|
|
||||||
{#each tickVals as tick, i (tick)}
|
|
||||||
{#if baseline === true}
|
|
||||||
<line class="baseline" y1={$height} y2={$height} x1="0" x2={$width} />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<g class="tick tick-{i}" transform="translate({$xScale(tick)},{Math.max(...$yRange)})">
|
|
||||||
{#if gridlines === true}
|
|
||||||
<line class="gridline" x1={halfBand} x2={halfBand} y1={-$height} y2="0" />
|
|
||||||
{/if}
|
|
||||||
{#if tickMarks === true}
|
|
||||||
<line
|
|
||||||
class="tick-mark"
|
|
||||||
x1={halfBand}
|
|
||||||
x2={halfBand}
|
|
||||||
y1={tickGutter}
|
|
||||||
y2={tickGutter + tickLen}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<text x={halfBand} y={tickGutter + tickLen} {dx} {dy} text-anchor={textAnchor(i, snapLabels)}
|
|
||||||
>{format(tick)}</text
|
|
||||||
>
|
|
||||||
</g>
|
|
||||||
{/each}
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.tick {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
line,
|
|
||||||
.tick line {
|
|
||||||
stroke: #aaa;
|
|
||||||
stroke-dasharray: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tick text {
|
|
||||||
fill: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tick .tick-mark,
|
|
||||||
.baseline {
|
|
||||||
stroke-dasharray: 0;
|
|
||||||
}
|
|
||||||
/* This looks slightly better */
|
|
||||||
.axis.snapLabels .tick:last-child text {
|
|
||||||
transform: translateX(3px);
|
|
||||||
}
|
|
||||||
.axis.snapLabels .tick.tick-0 text {
|
|
||||||
transform: translateX(-3px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
<!--
|
|
||||||
@component
|
|
||||||
Generates an SVG y-axis. This component is also configured to detect if your y-scale is an ordinal scale. If so, it will place the tickMarks in the middle of the bandwidth.
|
|
||||||
-->
|
|
||||||
<script>
|
|
||||||
import { getContext } from 'svelte';
|
|
||||||
|
|
||||||
const { xRange, yScale, width } = getContext('LayerCake');
|
|
||||||
|
|
||||||
/** @type {Boolean} [tickMarks=false] - Show marks next to the tick label. */
|
|
||||||
export let tickMarks = false;
|
|
||||||
|
|
||||||
/** @type {String} [labelPosition='even'] - Whether the label sits even with its value ('even') or sits on top ('above') the tick mark. Default is 'even'. */
|
|
||||||
export let labelPosition = 'even';
|
|
||||||
|
|
||||||
/** @type {Boolean} [snapBaselineLabel=false] - When labelPosition='even', adjust the lowest label so that it sits above the tick mark. */
|
|
||||||
export let snapBaselineLabel = false;
|
|
||||||
|
|
||||||
/** @type {Boolean} [gridlines=true] - Show gridlines extending into the chart area. */
|
|
||||||
export let gridlines = true;
|
|
||||||
|
|
||||||
/** @type {Number} [tickMarkLength=undefined] - The length of the tick mark. If not set, becomes the length of the widest tick. */
|
|
||||||
export let tickMarkLength = undefined;
|
|
||||||
|
|
||||||
/** @type {Function} [format=d => d] - A function that passes the current tick value and expects a nicely formatted value in return. */
|
|
||||||
export let format = d => d;
|
|
||||||
|
|
||||||
/** @type {Number|Array|Function} [ticks=4] - If this is a number, it passes that along to the [d3Scale.ticks](https://github.com/d3/d3-scale) function. If this is an array, hardcodes the ticks to those values. If it's a function, passes along the default tick values and expects an array of tick values in return. */
|
|
||||||
export let ticks = 4;
|
|
||||||
|
|
||||||
/** @type {Number} [tickGutter=0] - The amount of whitespace between the start of the tick and the chart drawing area (the xRange min). */
|
|
||||||
export let tickGutter = 2;
|
|
||||||
|
|
||||||
/** @type {Number} [dx=0] - Any optional value passed to the `dx` attribute on the text label. */
|
|
||||||
export let dx = 0;
|
|
||||||
|
|
||||||
/** @type {Number} [dy=0] - Any optional value passed to the `dy` attribute on the text label. */
|
|
||||||
export let dy = 0;
|
|
||||||
|
|
||||||
/** @type {Number} [charPixelWidth=7.25] - Used to calculate the widest label length to offset labels. Adjust if the automatic tick length doesn't look right because you have a bigger font (or just set `tickMarkLength` to a pixel value). */
|
|
||||||
export let charPixelWidth = 7.25;
|
|
||||||
|
|
||||||
$: isBandwidth = typeof $yScale.bandwidth === 'function';
|
|
||||||
|
|
||||||
$: tickVals = Array.isArray(ticks)
|
|
||||||
? ticks
|
|
||||||
: isBandwidth
|
|
||||||
? $yScale.domain()
|
|
||||||
: typeof ticks === 'function'
|
|
||||||
? ticks($yScale.ticks())
|
|
||||||
: $yScale.ticks(ticks);
|
|
||||||
|
|
||||||
function calcStringLength(sum, val) {
|
|
||||||
if (val === ',' || val === '.') return sum + charPixelWidth * 0.5;
|
|
||||||
return sum + charPixelWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
$: tickLen =
|
|
||||||
tickMarks === true
|
|
||||||
? labelPosition === 'above'
|
|
||||||
? tickMarkLength ?? widestTickLen
|
|
||||||
: tickMarkLength ?? 6
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
$: widestTickLen = Math.max(
|
|
||||||
10,
|
|
||||||
Math.max(...tickVals.map(d => format(d).toString().split('').reduce(calcStringLength, 0)))
|
|
||||||
);
|
|
||||||
|
|
||||||
$: x1 = -tickGutter - (labelPosition === 'above' ? widestTickLen : tickLen);
|
|
||||||
$: y = isBandwidth ? $yScale.bandwidth() / 2 : 0;
|
|
||||||
|
|
||||||
$: maxTickValPx = Math.max(...tickVals.map($yScale));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<g class="axis y-axis">
|
|
||||||
{#each tickVals as tick (tick)}
|
|
||||||
{@const tickValPx = $yScale(tick)}
|
|
||||||
<g class="tick tick-{tick}" transform="translate({$xRange[0]}, {tickValPx})">
|
|
||||||
{#if gridlines === true}
|
|
||||||
<line class="gridline" {x1} x2={$width} y1={y} y2={y}></line>
|
|
||||||
{/if}
|
|
||||||
{#if tickMarks === true}
|
|
||||||
<line class="tick-mark" {x1} x2={x1 + tickLen} y1={y} y2={y}></line>
|
|
||||||
{/if}
|
|
||||||
<text
|
|
||||||
x={x1}
|
|
||||||
{y}
|
|
||||||
dx={dx + (labelPosition === 'even' ? -3 : 0)}
|
|
||||||
text-anchor={labelPosition === 'above' ? 'start' : 'end'}
|
|
||||||
dy={dy +
|
|
||||||
(labelPosition === 'above' || (snapBaselineLabel === true && tickValPx === maxTickValPx)
|
|
||||||
? -3
|
|
||||||
: 4)}>{format(tick)}</text
|
|
||||||
>
|
|
||||||
</g>
|
|
||||||
{/each}
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.tick {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tick line {
|
|
||||||
stroke: #fff;
|
|
||||||
}
|
|
||||||
.tick .gridline {
|
|
||||||
stroke-dasharray: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tick text {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tick.tick-0 line {
|
|
||||||
stroke-dasharray: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<!--
|
|
||||||
@component
|
|
||||||
Generates an SVG bar chart.
|
|
||||||
-->
|
|
||||||
<script>
|
|
||||||
import { getContext } from 'svelte';
|
|
||||||
|
|
||||||
const { data, xGet, yGet, xScale, yScale } = getContext('LayerCake');
|
|
||||||
|
|
||||||
/** @type {String} [fill='#3B82F6'] - The shape's fill color. This is technically optional because it comes with a default value but you'll likely want to replace it with your own color. */
|
|
||||||
export let fill = '#fff';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<g class="bar-group">
|
|
||||||
{#each $data as d, i}
|
|
||||||
<rect
|
|
||||||
class="group-rect"
|
|
||||||
data-id={i}
|
|
||||||
x={$xScale.range()[0]}
|
|
||||||
y={$yGet(d)}
|
|
||||||
height={$yScale.bandwidth()}
|
|
||||||
width={$xGet(d)}
|
|
||||||
{fill}
|
|
||||||
></rect>
|
|
||||||
{/each}
|
|
||||||
</g>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user