update at 2026-02-12 19:18:45

This commit is contained in:
douboer@gmail.com
2026-02-12 19:18:45 +08:00
parent a64f38861c
commit 804936e97a
3 changed files with 1327 additions and 134 deletions

View File

@@ -7,24 +7,33 @@
</div>
<div class="toolbar">
<div class="tool-item theme-trigger">
<div ref="themeTriggerRef" class="tool-item theme-trigger">
<span class="tool-label">选择主题</span>
<button class="icon-btn" type="button" @click="toggleThemePicker">
<button class="icon-btn" type="button" @click.stop="toggleThemePicker">
<img :src="iconChooseColor" alt="choose-color" />
</button>
<div v-if="showThemePicker" class="theme-popover">
<div
v-if="showThemePicker"
ref="themePopoverRef"
class="theme-popover"
:style="themePopoverStyle"
@click.stop
>
<div class="theme-header">选择配色主题</div>
<div class="theme-list">
<div class="theme-wheel-wrap">
<div ref="themeWheelRef" class="theme-wheel" @scroll="onThemeWheelScroll">
<button
v-for="theme in themes"
v-for="(theme, index) in themes"
:key="theme.id"
class="theme-row"
:class="{ selected: selectedThemeId === theme.id }"
:style="{ opacity: getThemeRowOpacity(index) }"
type="button"
@click="pickTheme(theme.id)"
>
<img :src="selectedThemeId === theme.id ? iconRadioOn : iconRadioOff" alt="主题选择" />
<div class="palette">
<div class="palette" :style="{ gridTemplateColumns: `repeat(${theme.colors.length}, 1fr)` }">
<span
v-for="color in theme.colors"
:key="`${theme.id}-${color}`"
@@ -34,6 +43,8 @@
</div>
</button>
</div>
<div class="theme-mask" />
</div>
</div>
</div>
@@ -80,11 +91,17 @@
<h2>源数据</h2>
<div class="field-block">
<div class="field-title-wrap">
<img :src="iconExpand" alt="展开" class="expand-icon" />
<button class="field-title-wrap" type="button" @click="toggleSection('sourceData')">
<img
:src="sectionVisible.sourceData ? iconZhedie : iconExpand"
:alt="sectionVisible.sourceData ? '折叠' : '展开'"
class="expand-icon"
/>
<h3>数据列</h3>
</div>
</button>
<template v-if="sectionVisible.sourceData">
<div class="column-list">
<div
v-for="(header, index) in columnHeaders"
:key="`source-data-${index}`"
@@ -100,13 +117,21 @@
</button>
</div>
</div>
<div class="field-block">
<div class="field-title-wrap">
<img :src="iconExpand" alt="展开" class="expand-icon" />
<h3>描述列</h3>
</template>
</div>
<div class="field-block">
<button class="field-title-wrap" type="button" @click="toggleSection('sourceDesc')">
<img
:src="sectionVisible.sourceDesc ? iconZhedie : iconExpand"
:alt="sectionVisible.sourceDesc ? '折叠' : '展开'"
class="expand-icon"
/>
<h3>描述列</h3>
</button>
<template v-if="sectionVisible.sourceDesc">
<div class="column-list">
<div
v-for="(header, index) in columnHeaders"
:key="`source-desc-${index}`"
@@ -124,16 +149,24 @@
</button>
</div>
</div>
</template>
</div>
</article>
<article class="panel block-panel">
<h2>目标数据</h2>
<div class="field-block">
<div class="field-title-wrap">
<img :src="iconExpand" alt="展开" class="expand-icon" />
<button class="field-title-wrap" type="button" @click="toggleSection('targetDesc')">
<img
:src="sectionVisible.targetDesc ? iconZhedie : iconExpand"
:alt="sectionVisible.targetDesc ? '折叠' : '展开'"
class="expand-icon"
/>
<h3>描述列</h3>
</div>
</button>
<template v-if="sectionVisible.targetDesc">
<div class="column-list">
<div
v-for="(header, index) in columnHeaders"
:key="`target-desc-${index}`"
@@ -151,6 +184,8 @@
</button>
</div>
</div>
</template>
</div>
</article>
</section>
@@ -166,13 +201,35 @@
<option value="target-to-source">target -&gt; source</option>
</select>
</label>
<label>
<label class="slider-label">
间距
<input v-model.number="nodeGap" type="range" min="8" max="42" />
<div class="slider-track-wrap">
<span class="slider-value" :style="getSliderValueStyle(nodeGap, 8, 42)">{{ nodeGap }}</span>
<input
v-model.number="nodeGap"
class="slider-input"
:style="getSliderTrackStyle(nodeGap, 8, 42)"
type="range"
min="8"
max="42"
/>
</div>
</label>
<label>
<label class="slider-label">
边距
<input v-model.number="chartPadding" type="range" min="10" max="70" />
<div class="slider-track-wrap">
<span class="slider-value" :style="getSliderValueStyle(chartPadding, 10, 70)">
{{ chartPadding }}
</span>
<input
v-model.number="chartPadding"
class="slider-input"
:style="getSliderTrackStyle(chartPadding, 10, 70)"
type="range"
min="10"
max="70"
/>
</div>
</label>
</div>
</div>
@@ -180,8 +237,6 @@
<div v-if="buildError" class="error-text">{{ buildError }}</div>
<div v-if="parseError" class="error-text">{{ parseError }}</div>
<div class="example-line">示例{{ previewExample }}</div>
<div ref="chartRef" class="chart-area" />
<div v-if="buildWarnings.length > 0" class="warning-area">
@@ -198,7 +253,7 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect } from 'vue';
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect, type CSSProperties } from 'vue';
import * as echarts from 'echarts/core';
import type { EChartsOption } from 'echarts';
import { SankeyChart } from 'echarts/charts';
@@ -208,10 +263,12 @@ import {
applyDirection,
buildSankeyData,
parseDataFile,
parseXlsxBuffer,
type MappingConfig,
type RawTable,
type SankeyBuildResult
} from './core';
import { THEME_PRESETS } from './theme-presets';
import iconWebLogo from '../assets/icons/webicon.png';
import iconTitle from '../assets/icons/星程桑基图.svg';
import iconChooseColor from '../assets/icons/choose-color.svg';
@@ -226,33 +283,12 @@ import iconRadioOff from '../assets/icons/radiobutton-no.svg';
import iconCheckboxOn from '../assets/icons/checkbox.svg';
import iconCheckboxOff from '../assets/icons/checkbox-no.svg';
import iconExpand from '../assets/icons/expand.svg';
import iconZhedie from '../assets/icons/zhedie.svg';
echarts.use([SankeyChart, TooltipComponent, CanvasRenderer]);
/**
* 主题色板列表。
* 颜色来自 Figma 示例,保证与设计稿观感一致。
*/
const themes = [
{
id: 'morandi',
colors: ['#F4F1DE', '#EAB69F', '#E07A5F', '#8F5D5D', '#3D405B', '#5F797B', '#81B29A', '#9EB998']
},
{
id: 'purple',
colors: ['#F72585', '#B5179E', '#7209B7', '#560BAD', '#480CA8', '#3A0CA3', '#3F37C9', '#4895EF', '#4CC9F0']
},
{
id: 'fog',
colors: ['#E8EDDF', '#CFDBD5', '#B7B7A4', '#A5A58D', '#6B705C', '#4F5D75', '#5D576B', '#6D597A']
},
{
id: 'sunset',
colors: ['#355070', '#515575', '#6D597A', '#915F78', '#B56576', '#CD6873', '#E56B6F', '#E88C7D', '#EAAC8B']
}
] as const;
const selectedThemeId = ref<(typeof themes)[number]['id']>('purple');
const themes = THEME_PRESETS;
const selectedThemeId = ref<string>('figma-violet');
const showThemePicker = ref(false);
const uploadMessage = ref('点击上传或将csv/xls文件拖到这里上传');
const parseError = ref('');
@@ -260,7 +296,14 @@ const buildError = ref('');
const chartRef = ref<HTMLDivElement | null>(null);
const fileInputRef = ref<HTMLInputElement | null>(null);
const themeWheelRef = ref<HTMLDivElement | null>(null);
const themeTriggerRef = ref<HTMLDivElement | null>(null);
const themePopoverRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.EChartsType | null = null;
let themeWheelRafId = 0;
const themePopoverStyle = ref<CSSProperties>({});
const THEME_ROW_HEIGHT = 42;
const rawTable = ref<RawTable | null>(null);
const buildResult = ref<SankeyBuildResult | null>(null);
@@ -275,8 +318,22 @@ const mapping = reactive<MappingConfig>({
const direction = ref<'source-to-target' | 'target-to-source'>('source-to-target');
const nodeGap = ref(24);
const chartPadding = ref(30);
/**
* 左侧字段区块的展开/折叠状态。
* true 表示展开false 表示折叠。
*/
const sectionVisible = reactive({
sourceData: true,
sourceDesc: true,
targetDesc: true
});
const selectedTheme = computed(() => themes.find((item) => item.id === selectedThemeId.value) ?? themes[0]);
const selectedThemeIndex = computed(() => {
const index = themes.findIndex((item) => item.id === selectedThemeId.value);
return index >= 0 ? index : 0;
});
const selectedTheme = computed(() => themes[selectedThemeIndex.value] ?? themes[0]);
const columnHeaders = computed(() => {
const headers = rawTable.value?.headers ?? [];
@@ -289,25 +346,6 @@ const columnHeaders = computed(() => {
const buildWarnings = computed(() => buildResult.value?.meta.warnings.slice(0, 8) ?? []);
const previewExample = computed(() => {
const table = rawTable.value;
if (!table || table.rows.length === 0) {
return '宁波北欧10-2582 -> 嘉兴四级算力池-11623-小模型';
}
const firstRow = table.rows[0];
const sourceParts =
mapping.sourceDescriptionColumns.length > 0
? mapping.sourceDescriptionColumns.map((column) => firstRow[column] ?? '').filter(Boolean)
: [firstRow[mapping.sourceDataColumn ?? 0] ?? ''];
const targetParts = mapping.targetDescriptionColumns
.map((column) => firstRow[column] ?? '')
.filter(Boolean);
return `${sourceParts.join(mapping.delimiter)} -> ${targetParts.join(mapping.delimiter)}`;
});
const chartNodes = computed(() => {
const links = buildResult.value ? applyDirection(buildResult.value.links, direction.value) : [];
const names = new Set<string>();
@@ -423,14 +461,158 @@ function closeThemePicker(): void {
}
function toggleThemePicker(): void {
showThemePicker.value = !showThemePicker.value;
const nextVisible = !showThemePicker.value;
showThemePicker.value = nextVisible;
if (nextVisible) {
void nextTick(() => {
updateThemePopoverPosition();
centerThemeByIndex(selectedThemeIndex.value, false);
});
}
}
function pickTheme(themeId: (typeof themes)[number]['id']): void {
function pickTheme(themeId: string): void {
selectedThemeId.value = themeId;
showThemePicker.value = false;
}
/**
* 点击弹窗外部时关闭主题窗口,保持和设计稿一致的关闭行为。
*/
function handleGlobalPointerDown(event: PointerEvent): void {
if (!showThemePicker.value) {
return;
}
const target = event.target as Node | null;
if (!target) {
return;
}
const popover = themePopoverRef.value;
const trigger = themeTriggerRef.value;
if (popover?.contains(target) || trigger?.contains(target)) {
return;
}
showThemePicker.value = false;
}
/**
* 配色滚轮滚动时,自动将“可视中心行”设为选中项。
*/
function onThemeWheelScroll(): void {
if (themeWheelRafId !== 0) {
cancelAnimationFrame(themeWheelRafId);
}
themeWheelRafId = requestAnimationFrame(() => {
const container = themeWheelRef.value;
if (!container) {
return;
}
const centerIndex = Math.round(container.scrollTop / THEME_ROW_HEIGHT);
const safeIndex = Math.max(0, Math.min(themes.length - 1, centerIndex));
selectedThemeId.value = themes[safeIndex]?.id ?? selectedThemeId.value;
});
}
/**
* 让指定主题滚动到中间行,匹配 Figma 选择器交互。
*/
function centerThemeByIndex(index: number, smooth: boolean): void {
const container = themeWheelRef.value;
if (!container) {
return;
}
const targetTop = index * THEME_ROW_HEIGHT;
const maxTop = Math.max(0, container.scrollHeight - container.clientHeight);
const safeTop = Math.max(0, Math.min(maxTop, targetTop));
container.scrollTo({
top: safeTop,
behavior: smooth ? 'smooth' : 'auto'
});
}
/**
* 主题弹窗使用 fixed 定位并在视口内钳制,避免窄窗口时被裁切。
* 同时动态设置顶部箭头位置,确保箭头对准主题按钮中心。
*/
function updateThemePopoverPosition(): void {
if (!showThemePicker.value) {
return;
}
const trigger = themeTriggerRef.value;
const popover = themePopoverRef.value;
if (!trigger || !popover) {
return;
}
const triggerRect = trigger.getBoundingClientRect();
const popoverWidth = popover.offsetWidth || 292;
const viewportWidth = window.innerWidth;
const safePadding = 8;
let left = triggerRect.left + triggerRect.width / 2 - popoverWidth / 2;
left = Math.max(safePadding, Math.min(viewportWidth - popoverWidth - safePadding, left));
const top = triggerRect.bottom + 8;
const triggerCenterX = triggerRect.left + triggerRect.width / 2;
const arrowLeft = Math.max(20, Math.min(popoverWidth - 20, triggerCenterX - left));
themePopoverStyle.value = {
left: `${left}px`,
top: `${top}px`,
'--theme-arrow-left': `${arrowLeft}px`
};
}
/**
* 选中项不透明度为 100%,其它项随着距离中心递减。
*/
function getThemeRowOpacity(index: number): number {
const distance = Math.abs(index - selectedThemeIndex.value);
if (distance === 0) {
return 1;
}
if (distance === 1) {
return 0.75;
}
if (distance === 2) {
return 0.5;
}
return 0.3;
}
/**
* 计算滑块上方数字气泡位置,让数字随滑块移动。
*/
function getSliderValueStyle(value: number, min: number, max: number): CSSProperties {
const range = Math.max(1, max - min);
const ratio = Math.max(0, Math.min(1, (value - min) / range));
const thumbWidth = 18;
const offset = (0.5 - ratio) * thumbWidth;
return {
left: `calc(${ratio * 100}% + ${offset}px)`
};
}
/**
* 生成滑条左右双色背景:左侧主色,右侧灰色。
*/
function getSliderTrackStyle(value: number, min: number, max: number): CSSProperties {
const range = Math.max(1, max - min);
const ratio = Math.max(0, Math.min(1, (value - min) / range));
return {
'--slider-percent': `${ratio * 100}%`
};
}
function toggleSourceDescription(column: number): void {
if (mapping.sourceDescriptionColumns.includes(column)) {
mapping.sourceDescriptionColumns = mapping.sourceDescriptionColumns.filter((item) => item !== column);
@@ -440,6 +622,13 @@ function toggleSourceDescription(column: number): void {
mapping.sourceDescriptionColumns = [...mapping.sourceDescriptionColumns, column].sort((a, b) => a - b);
}
/**
* 切换左侧区块展开状态,支持源数据、源描述、目标描述。
*/
function toggleSection(section: keyof typeof sectionVisible): void {
sectionVisible[section] = !sectionVisible[section];
}
function toggleTargetDescription(column: number): void {
if (mapping.targetDescriptionColumns.includes(column)) {
mapping.targetDescriptionColumns = mapping.targetDescriptionColumns.filter((item) => item !== column);
@@ -502,6 +691,31 @@ async function onDropFile(event: DragEvent): Promise<void> {
await loadDataFile(file);
}
/**
* 页面首次进入时默认加载示例文件,确保用户无需上传即可看到效果。
*/
async function loadDefaultExampleFile(): Promise<void> {
if (rawTable.value) {
return;
}
try {
const response = await fetch('/data/example0.xlsx', { cache: 'no-store' });
if (!response.ok) {
throw new Error(`示例文件加载失败: HTTP ${response.status}`);
}
const buffer = await response.arrayBuffer();
const parsed = parseXlsxBuffer(buffer);
rawTable.value = parsed;
setDefaultMappingByColumns(parsed.headers.length);
uploadMessage.value = `已加载: example0.xlsx${parsed.rows.length} 行)`;
parseError.value = '';
} catch (error) {
parseError.value = error instanceof Error ? error.message : '示例文件加载失败';
}
}
function formatFileTimestamp(): string {
const now = new Date();
const pad = (value: number) => String(value).padStart(2, '0');
@@ -563,12 +777,23 @@ onMounted(() => {
chartInstance = echarts.init(container, undefined, { renderer: 'canvas' });
chartInstance.setOption(chartOption.value);
void loadDefaultExampleFile();
window.addEventListener('resize', syncChartSize);
window.addEventListener('resize', updateThemePopoverPosition);
window.addEventListener('scroll', updateThemePopoverPosition, true);
window.addEventListener('pointerdown', handleGlobalPointerDown);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', syncChartSize);
window.removeEventListener('resize', updateThemePopoverPosition);
window.removeEventListener('scroll', updateThemePopoverPosition, true);
window.removeEventListener('pointerdown', handleGlobalPointerDown);
chartInstance?.dispose();
chartInstance = null;
if (themeWheelRafId !== 0) {
cancelAnimationFrame(themeWheelRafId);
themeWheelRafId = 0;
}
});
</script>

View File

@@ -149,23 +149,20 @@ body {
}
.theme-popover {
position: absolute;
top: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
width: 280px;
position: fixed;
width: 292px;
border: 1px solid var(--primary-7);
border-radius: 24px 24px 0 0;
background: #fff;
z-index: 10;
padding: 8px;
padding: 8px 8px 10px;
}
.theme-popover::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
left: var(--theme-arrow-left, 50%);
width: 14px;
height: 14px;
background: #fff;
@@ -177,13 +174,53 @@ body {
.theme-header {
text-align: center;
color: var(--primary-6);
font-size: 14px;
margin-bottom: 8px;
font-size: 15px;
margin-bottom: 6px;
}
.theme-list {
max-height: 180px;
overflow: auto;
.theme-wheel-wrap {
--theme-row-height: 42px;
position: relative;
height: calc(var(--theme-row-height) * 5);
overflow: hidden;
}
.theme-wheel {
height: 100%;
overflow-y: auto;
padding-block: calc(var(--theme-row-height) * 2);
margin: 0;
scrollbar-width: thin;
scrollbar-color: #d6d8dc #fff;
}
.theme-wheel::-webkit-scrollbar {
width: 6px;
}
.theme-wheel::-webkit-scrollbar-track {
background: #fff;
}
.theme-wheel::-webkit-scrollbar-thumb {
background: #d6d8dc;
border-radius: 999px;
}
.theme-mask {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 3;
background: linear-gradient(
180deg,
#ffffff 0%,
rgba(255, 255, 255, 0.74) 20%,
rgba(255, 255, 255, 0) 35%,
rgba(255, 255, 255, 0) 65%,
rgba(255, 255, 255, 0.74) 80%,
#ffffff 100%
);
}
.theme-row {
@@ -193,8 +230,14 @@ body {
display: flex;
align-items: center;
gap: 8px;
padding: 6px;
height: 42px;
padding: 5px 6px;
cursor: pointer;
transition: opacity 0.2s ease;
}
.theme-row.selected {
opacity: 1;
}
.theme-row img {
@@ -205,8 +248,28 @@ body {
.palette {
flex: 1;
display: grid;
grid-template-columns: repeat(9, 1fr);
height: 20px;
border-radius: 2px;
overflow: hidden;
position: relative;
}
/* 选中分割线挂在色条本身,滚动时与选中项一起移动,长度与色条完全一致。 */
.theme-row.selected .palette::before,
.theme-row.selected .palette::after {
content: '';
position: absolute;
left: 0;
right: 0;
border-top: 1px solid var(--fill-3);
}
.theme-row.selected .palette::before {
top: 0;
}
.theme-row.selected .palette::after {
bottom: 0;
}
.palette-cell {
@@ -245,13 +308,20 @@ body {
.field-block {
margin-top: 12px;
position: relative;
}
.field-title-wrap {
width: 100%;
border: 0;
background: transparent;
padding: 0;
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
cursor: pointer;
text-align: left;
}
.field-title-wrap h3 {
@@ -261,17 +331,54 @@ body {
}
.expand-icon {
width: 16px;
height: 16px;
width: 20px;
height: 20px;
}
.column-list {
position: relative;
--connector-x: 10px;
--icon-left: 20px;
--row-height: 32px;
--connector-top-offset: -6px;
}
/* 展开后显示分类到列表项的树状连线,严格限制到首尾项中线。 */
.column-list::before {
content: '';
position: absolute;
left: var(--connector-x);
top: var(--connector-top-offset);
bottom: calc(var(--row-height) / 2);
border-left: 1px solid var(--fill-4);
}
.column-row {
position: relative;
display: flex;
align-items: center;
gap: 8px;
border-bottom: 1px solid var(--fill-4);
height: 32px;
padding-bottom: 6px;
padding-left: var(--icon-left);
}
.column-row::before {
content: '';
position: absolute;
left: var(--connector-x);
top: calc(50% - 0.5px);
width: calc(var(--icon-left) - var(--connector-x));
border-top: 1px solid var(--fill-4);
}
.column-row::after {
content: '';
position: absolute;
left: var(--icon-left);
right: 0;
bottom: 0;
border-top: 1px solid var(--fill-4);
}
.column-icon {
@@ -330,8 +437,82 @@ body {
font-size: 12px;
}
.preview-controls select,
.preview-controls input {
.preview-controls .slider-label {
align-items: center;
color: #000;
font-size: 14px;
}
.slider-track-wrap {
position: relative;
width: 130px;
height: 18px;
}
.slider-input {
width: 100%;
height: 18px;
margin: 0;
background: transparent;
-webkit-appearance: none;
appearance: none;
}
.slider-input::-webkit-slider-runnable-track {
height: 4px;
border-radius: 999px;
background: linear-gradient(
to right,
#8552a1 0,
#8552a1 var(--slider-percent, 0%),
#d9d9d9 var(--slider-percent, 0%),
#d9d9d9 100%
);
}
.slider-input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
border: 1px solid #d9d9d9;
background: #fff;
margin-top: -7px;
}
.slider-input::-moz-range-track {
height: 4px;
border-radius: 999px;
background: #d9d9d9;
}
.slider-input::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
border: 1px solid #d9d9d9;
background: #fff;
}
.slider-input::-moz-range-progress {
height: 4px;
border-radius: 999px;
background: #8552a1;
}
.slider-value {
position: absolute;
top: -13px;
transform: translateX(-50%);
font-size: 10px;
line-height: 1;
color: var(--primary-6);
white-space: nowrap;
pointer-events: none;
}
.preview-controls select {
accent-color: var(--primary-7);
}

787
src/theme-presets.ts Normal file
View File

@@ -0,0 +1,787 @@
/**
* Figma 节点 3267:86 的全部配色预设。
* 每组颜色按设计稿色带从左到右排列。
*/
export interface ThemePreset {
id: string;
name: string;
colors: string[];
}
export const THEME_PRESETS: ThemePreset[] = [
{
"id": "figma-aegean",
"name": "Figma-Aegean",
"colors": [
"#264653",
"#287271",
"#2A9D8F",
"#8AB17D",
"#E9C46A",
"#EFB366",
"#F4A261",
"#F1965D",
"#EE8959",
"#E76F51"
]
},
{
"id": "figma-violet",
"name": "Figma-Violet",
"colors": [
"#F72585",
"#B5179E",
"#7209B7",
"#560BAD",
"#480CA8",
"#3A0CA3",
"#3F37C9",
"#4361EE",
"#4895EF",
"#4CC9F0"
]
},
{
"id": "figma-1",
"name": "Figma-01",
"colors": [
"#322F4F",
"#433E71",
"#554C93",
"#98958C",
"#DBDD85",
"#D8DD7D",
"#D5DB74",
"#CED56E",
"#C8CF67",
"#BAC35A"
]
},
{
"id": "figma-2",
"name": "Figma-02",
"colors": [
"#2B3B51",
"#355971",
"#3F7690",
"#91A483",
"#E2D075",
"#E4C66F",
"#E4BD69",
"#E0B464",
"#DBAA5F",
"#D19654"
]
},
{
"id": "figma-3",
"name": "Figma-03",
"colors": [
"#274D4C",
"#2B7171",
"#2F9595",
"#8B9395",
"#E79094",
"#EC878A",
"#EF7D7F",
"#EC7578",
"#E86D6F",
"#E15D5F"
]
},
{
"id": "figma-4",
"name": "Figma-04",
"colors": [
"#3D3D3D",
"#5B5B5B",
"#797979",
"#9F9F9F",
"#C4C4C4",
"#BCBCBC",
"#B3B3B3",
"#AAAAAA",
"#A1A1A1",
"#8F8F8F"
]
},
{
"id": "figma-5",
"name": "Figma-05",
"colors": [
"#F4F1DE",
"#EAB69F",
"#E07A5F",
"#8F5D5D",
"#3D405B",
"#5F797B",
"#81B29A",
"#9EB998",
"#BABF95",
"#F2CC8F"
]
},
{
"id": "figma-6",
"name": "Figma-06",
"colors": [
"#F2F3E3",
"#D6DAA5",
"#B9C167",
"#7C805D",
"#3E3D52",
"#68667A",
"#938FA1",
"#A8A6A1",
"#BBBBA1",
"#E3E6A1"
]
},
{
"id": "figma-7",
"name": "Figma-07",
"colors": [
"#F3F1E0",
"#E0C7A1",
"#CD9B62",
"#856D5D",
"#3D3F57",
"#63707A",
"#89A19D",
"#A2B09C",
"#BABD9A",
"#EBD896"
]
},
{
"id": "figma-8",
"name": "Figma-08",
"colors": [
"#F3E6E7",
"#E7A8A9",
"#DA6A6B",
"#8C5D5D",
"#3D4F4E",
"#607A7A",
"#83A4A5",
"#9FA6A7",
"#BAA7A8",
"#F0A9AB"
]
},
{
"id": "figma-9",
"name": "Figma-09",
"colors": [
"#EFEFEF",
"#C2C2C2",
"#959595",
"#6B6B6B",
"#424242",
"#717171",
"#A0A0A0",
"#ADADAD",
"#B8B8B8",
"#D0D0D0"
]
},
{
"id": "figma-10",
"name": "Figma-10",
"colors": [
"#A8B868",
"#798575",
"#4A5282",
"#393F7C",
"#313679",
"#282C75",
"#3C3C9D",
"#4E4CC3",
"#645FD4",
"#7A71E4"
]
},
{
"id": "figma-11",
"name": "Figma-11",
"colors": [
"#EF476F",
"#F78C6B",
"#FFD166",
"#83D483",
"#06D6A0",
"#001914",
"#118AB2",
"#0F7799",
"#0C637F",
"#073B4C"
]
},
{
"id": "figma-12",
"name": "Figma-12",
"colors": [
"#D06A79",
"#984B8D",
"#5E2BA1",
"#482398",
"#3D1F94",
"#311B90",
"#3D39B6",
"#4857DC",
"#567BE3",
"#629FEB"
]
},
{
"id": "figma-13",
"name": "Figma-13",
"colors": [
"#B0BC63",
"#CED674",
"#EDF186",
"#A19B9B",
"#5444B0",
"#493DAB",
"#3E35A6",
"#362E8E",
"#2C2676",
"#1A1646"
]
},
{
"id": "figma-14",
"name": "Figma-14",
"colors": [
"#D07E6A",
"#E3AF6F",
"#F6E073",
"#91B98D",
"#2C90A6",
"#2A79A9",
"#2761AD",
"#225494",
"#1B467B",
"#102949"
]
},
{
"id": "figma-15",
"name": "Figma-15",
"colors": [
"#E65D5C",
"#F1797A",
"#FC9498",
"#87A6A9",
"#10B7B9",
"#14ACAC",
"#17A09F",
"#148A88",
"#107271",
"#094443"
]
},
{
"id": "figma-16",
"name": "Figma-16",
"colors": [
"#7D7D7D",
"#A8A8A8",
"#D2D2D2",
"#B2B2B2",
"#919191",
"#7E7E7E",
"#6A6A6A",
"#5B5B5B",
"#4C4C4C",
"#2D2D2D"
]
},
{
"id": "figma-17",
"name": "Figma-17",
"colors": [
"#5F0F40",
"#7D092F",
"#9A031E",
"#CB4721",
"#FB8B24",
"#EF781C",
"#E36414",
"#AE5E26",
"#795838",
"#0F4C5C"
]
},
{
"id": "figma-18",
"name": "Figma-18",
"colors": [
"#414731",
"#515A23",
"#616C15",
"#99A32C",
"#D1D942",
"#C2CB37",
"#B3BC2C",
"#909636",
"#6C6F41",
"#252157"
]
},
{
"id": "figma-19",
"name": "Figma-19",
"colors": [
"#502939",
"#672F2A",
"#7E351A",
"#B27225",
"#E6B030",
"#D99F27",
"#CB8E1E",
"#9F782D",
"#72623C",
"#1A375A"
]
},
{
"id": "figma-20",
"name": "Figma-20",
"colors": [
"#5B2A28",
"#771E1C",
"#921211",
"#C43133",
"#F55054",
"#E94347",
"#DC363A",
"#AA3E40",
"#774547",
"#125554"
]
},
{
"id": "figma-21",
"name": "Figma-21",
"colors": [
"#2C2C2C",
"#303030",
"#333333",
"#6A6A6A",
"#A0A0A0",
"#919191",
"#808080",
"#6F6F6F",
"#5E5E5E",
"#3B3B3B"
]
},
{
"id": "figma-22",
"name": "Figma-22",
"colors": [
"#366260",
"#516765",
"#6C6B6A",
"#8E6D6C",
"#B16E6D",
"#C76E6D",
"#DE6D6D",
"#E38384",
"#E6999A"
]
},
{
"id": "figma-23",
"name": "Figma-23",
"colors": [
"#39476C",
"#515371",
"#695F75",
"#876F74",
"#A67F73",
"#BA8971",
"#CE936E",
"#D7AA7E",
"#DEC08F"
]
},
{
"id": "figma-24",
"name": "Figma-24",
"colors": [
"#3F3D66",
"#52526B",
"#656770",
"#7E8270",
"#979D70",
"#A7AE6F",
"#B7C06D",
"#C5CC81",
"#D2D794"
]
},
{
"id": "figma-25",
"name": "Figma-25",
"colors": [
"#4B4B4B",
"#575757",
"#626262",
"#707070",
"#7E7E7E",
"#878787",
"#8F8F8F",
"#A5A5A5",
"#BABABA"
]
},
{
"id": "figma-26",
"name": "Figma-26",
"colors": [
"#363636",
"#474747",
"#585858",
"#9C9C9C",
"#E0E0E0",
"#A5A5A5",
"#696969",
"#565656",
"#424242"
]
},
{
"id": "figma-27",
"name": "Figma-27",
"colors": [
"#293B3B",
"#235959",
"#1D7575",
"#84A6A6",
"#ECD7D8",
"#D58A8A",
"#BD3C3D",
"#993333",
"#732829"
]
},
{
"id": "figma-28",
"name": "Figma-28",
"colors": [
"#2B363B",
"#2B4559",
"#295477",
"#899BA6",
"#EAE2D5",
"#CBA886",
"#AC6F37",
"#8B5B2F",
"#694626"
]
},
{
"id": "figma-29",
"name": "Figma-29",
"colors": [
"#2F2E3B",
"#353159",
"#3A3376",
"#908EA6",
"#E7E8D6",
"#BEC388",
"#949D3A",
"#788031",
"#5B6127"
]
},
{
"id": "figma-30",
"name": "Figma-30",
"colors": [
"#283D3B",
"#21585A",
"#197278",
"#83A8A6",
"#EDDDD4",
"#D99185",
"#E9B5AF",
"#9E3A2E",
"#772E25"
]
},
{
"id": "figma-31",
"name": "Figma-31",
"colors": [
"#BDBDBD",
"#9D9D9D",
"#7E7E7E",
"#6B6B6B",
"#585858",
"#5D5D5D",
"#606060",
"#626262",
"#707070",
"#7D7D7D"
]
},
{
"id": "figma-32",
"name": "Figma-32",
"colors": [
"#FB5860",
"#F74046",
"#F2292C",
"#F23433",
"#F23E39",
"#B86E68",
"#9C867F",
"#7F9E96",
"#5FB4AE",
"#3DCAC5"
]
},
{
"id": "figma-33",
"name": "Figma-33",
"colors": [
"#F3D321",
"#E7B019",
"#DC8C10",
"#D67039",
"#D05460",
"#A2529A",
"#8C51B8",
"#7550D5",
"#5F5FE3",
"#476CEF"
]
},
{
"id": "figma-34",
"name": "Figma-34",
"colors": [
"#E6EB40",
"#D1DA2F",
"#BDC91E",
"#AEBE36",
"#9FB24D",
"#838F81",
"#757E9C",
"#666CB6",
"#5F5FC8",
"#5650DA"
]
},
{
"id": "figma-35",
"name": "Figma-35",
"colors": [
"#FFBE0B",
"#FD8A09",
"#FB5607",
"#FD2B3B",
"#FF006E",
"#C11CAD",
"#A22ACD",
"#8338EC",
"#5F5FF6",
"#3A86FF"
]
},
{
"id": "figma-36",
"name": "Figma-36",
"colors": [
"#6E6E6E",
"#555555",
"#3C3C3C",
"#333333",
"#2F2F2F",
"#2A2A2A",
"#4A4A4A",
"#686868",
"#888888",
"#A8A8A8"
]
},
{
"id": "figma-37",
"name": "Figma-37",
"colors": [
"#EC5B57",
"#AD635D",
"#6C6B64",
"#526660",
"#45645D",
"#37615B",
"#3E8983",
"#44B0AB",
"#4BC8C4",
"#52DFDD"
]
},
{
"id": "figma-38",
"name": "Figma-38",
"colors": [
"#05668D",
"#04738F",
"#028090",
"#019493",
"#00A896",
"#01B698",
"#02C39A",
"#79DBAC",
"#B5E7B5",
"#F0F3BD"
]
},
{
"id": "figma-39",
"name": "Figma-39",
"colors": [
"#292281",
"#2D2586",
"#31278B",
"#382D93",
"#3F329B",
"#4437A1",
"#4A3BA6",
"#9D96BA",
"#C7C4C4",
"#F1F0CD"
]
},
{
"id": "figma-40",
"name": "Figma-40",
"colors": [
"#164588",
"#184E8B",
"#19568E",
"#1B6393",
"#1E7098",
"#22799B",
"#25829F",
"#8ABAB1",
"#BED6BB",
"#F0F2C3"
]
},
{
"id": "figma-41",
"name": "Figma-41",
"colors": [
"#097C7A",
"#098281",
"#088988",
"#089393",
"#089D9E",
"#0AA4A6",
"#0BABAD",
"#7DC0C2",
"#B7CACC",
"#F0D4D6"
]
},
{
"id": "figma-42",
"name": "Figma-42",
"colors": [
"#4D4D4D",
"#555555",
"#5C5C5C",
"#676767",
"#737373",
"#7C7C7C",
"#848484",
"#B8B8B8",
"#D2D2D2",
"#EBEBEB"
]
},
{
"id": "figma-43",
"name": "Figma-43",
"colors": [
"#16697A",
"#2F8498",
"#489FB5",
"#65B0C1",
"#9DCAD2",
"#B8D4D8",
"#EDE7E3",
"#F2D7B5",
"#F6C787",
"#FFA62B"
]
},
{
"id": "figma-44",
"name": "Figma-44",
"colors": [
"#352E74",
"#4E4892",
"#6862AE",
"#817BBB",
"#ADAACF",
"#C2C0D6",
"#EAEBE4",
"#E7E9BF",
"#E4E79A",
"#DDE44F"
]
},
{
"id": "figma-45",
"name": "Figma-45",
"colors": [
"#254D77",
"#3E6795",
"#5782B2",
"#7297BE",
"#A5BBD1",
"#BDCAD7",
"#EBE8E3",
"#EDDFB9",
"#EDD68F",
"#EEC33A"
]
},
{
"id": "figma-46",
"name": "Figma-46",
"colors": [
"#1A7271",
"#338F8E",
"#4CABAA",
"#68B9B8",
"#9FCECE",
"#B9D6D6",
"#ECE4E4",
"#F0C3C5",
"#F3A2A5",
"#FA6065"
]
},
{
"id": "figma-47",
"name": "Figma-47",
"colors": [
"#525252",
"#6C6C6C",
"#878787",
"#9B9B9B",
"#BDBDBD",
"#CCCCCC",
"#E8E8E8",
"#DBDBDB",
"#CDCDCD",
"#B2B2B2"
]
}
];