feat(sw): implement SW header design system
No files matched your search
@@ -0,0 +1,81 @@
|
||||
# @hm-design/sw
|
||||
|
||||
> **3차 구현(2026-10-16):** `hiddenControls`(로고 외 모든 구성요소 개별 제거, 로고 필수), `menuOrder`(레이어·색상·선/문자/치수스타일 순서), 속성창 `shapeContent` Slot, 속성창 4상태 개별·비교 Story를 추가했습니다. 미지정 시 기존 디자인과 같습니다. [사용법](../../docs/sw/sw-header-usage.md#구성요소-표시메뉴-순서속성창-4상태형상-slot-2026-10-16-3차), [3차 검증 기록](../../docs/sw/sw-header-composition-shape-verification-2026-10-16.md).
|
||||
|
||||
> **후속 구현(2026-10-16):** 속성Bar(`SWHeaderPropertyBar`), 메뉴 단위 `controls` allowlist, Colorize 원본 Default/Hover를 추가했습니다. 상세 pill의 미사용 Stroke 선언·변수는 원본에서 제거했습니다. [사용법](../../docs/sw/sw-header-usage.md#후속-5개-요구사항-연결-2026-10-16), [새 검증 기록](../../docs/sw/sw-header-followup-five-verification-2026-10-16.md)을 따릅니다. 이전 설명과 PASS 로그는 보존합니다.
|
||||
|
||||
> **최신 추가 구현(2026-10-16):** 상세 5개 Hover·색상 기준 필수 선택·목록 Hover/Selected 통일·데이터 기반 속성창·K 가이드 반복을 제공합니다. 아래 추가 연결과 [현재 검증 보고](../../docs/sw/sw-header-additional-verification-2026-10-16.md)를 따릅니다. 아래의 이전 Hover 없음·일반 자기 색 Stroke 설명은 추가 기준으로 대체됩니다.
|
||||
|
||||
HM Design System의 SW(설치형·도면 SW) 전용 React 컴포넌트 패키지입니다. `@hm-design/ui`와 독립적으로 빌드·배포·사용하며 서로 의존하지 않습니다.
|
||||
|
||||
현재 포함: **SW Header** (Figma `rj6wpsfk24lOWvWiVRAANP` node `26:8944` `header_green`), `SWTheme`.
|
||||
|
||||
```tsx
|
||||
import { SWHeader } from "@hm-design/sw"
|
||||
import "@hm-design/sw/styles.css"
|
||||
|
||||
<SWHeader documentTitle="C0020101-006-평면및종단면도(06)" logoSrc={productLogo} logoAlt="제품명" onClose={closeWindow} />
|
||||
```
|
||||
|
||||
- 로고는 확정 디자인이 아닙니다. 로고 영역 배치는 공통이고 로고 이미지는 각 SW 프로그램이 `logoSrc` 또는 `logo`로 전달합니다. 패키지에는 로고가 없으며, 전달하지 않으면 로고 없이 표시됩니다.
|
||||
|
||||
- 파일명을 클릭하면 문서 Dropdown(Figma 26:10662)이 열립니다. 목록은 `documents`, 선택·New·Open은 Callback으로 각 프로그램이 연결합니다(`documents` 미전달 시 Dropdown 없음).
|
||||
- 레이어 필드를 클릭하면 레이어 Dropdown(Figma 26:10940, 갈색 목록·주황 행 Hover)이 열립니다. 목록은 `layers`(`id`, `name`, `color`, `visible`, `frozen`, `locked`, `disabled`), 현재 레이어는 `activeLayerId`이며 선택은 `onLayerSelect`, 전구·눈꽃·자물쇠는 `onLayerVisibilityChange` / `onLayerFreezeChange` / `onLayerLockChange`, 하단 버튼은 `onLayerDetail`로 각 프로그램이 연결합니다. 아이콘은 데이터만 반영하며 실제 표시·동결·잠금은 호스트가 처리합니다(`layers` 미전달 시 기존 필드 버튼). 확정 매핑: 전구 컬러 `visible=true`·회색 `false`, 눈꽃 컬러 `frozen=false`·회색 `true`, 자물쇠 컬러 `locked=false`·회색 `true`이며 클릭 시 각 boolean의 반전값을 전달합니다([명세 §5.2](../../docs/sw/sw-header-design-spec.md#52-레이어-dropdown-figma-headermenu레이어-2610940-2026-10-16), [검증 기록](../../docs/sw/sw-header-layer-dropdown-verification-2026-10-16.md)).
|
||||
- Hover는 Figma 홈 버튼의 노란 gradient 1px 테두리를 SW Header의 테두리 있는 버튼에 공통 적용합니다(디자인 명세 §11.1).
|
||||
- 창 이동 Hover Motion은 GIF의 경로를 유지하고 후속 사용자 요청으로 1.64 → **1.4초**로 단축했습니다. 커서 상승 → 약 444ms 문구 등장 → 오른쪽 위로 이동 → 약 888ms부터 512ms 유지 → 반복입니다. 기존 SVG·줄무늬·Tooltip·`onWindowMoveStart`·Drag 중 정지·Reduced Motion·88px/24px/숨김 정책은 유지합니다. 실제 창 이동은 호스트 책임이며 [사용 가이드 §10.2](../../docs/sw/sw-header-usage.md#102-창-이동-hover-motion), [현재 검증 기록](../../docs/sw/sw-header-speed-composed-hover-verification-2026-10-16.md)을 따릅니다.
|
||||
- 그룹 필드 Hover는 텍스트와 선/폰트/치수 미리보기를 `#FFC600`으로 표시합니다. 기존 Gradient Border·검정 외곽선·색상 견본·레이어 상태·Disabled/Selected 정책을 유지하며 상세보기는 기존 텍스트 전용 Hover 그대로입니다([명세 §11.5](../../docs/sw/sw-header-design-spec.md#115-현재-추가-기준--1400ms-및-그룹-필드-hover-2026-10-16)).
|
||||
- 색상·선스타일·문자스타일·치수스타일 Dropdown(2026-10-16, Figma 26:10986 / 26:11036 / 26:12666 / 26:12659 / 26:11104 / 26:10702 / 26:11181 / 26:11215): `colors`·`userColors`·`colorMode`, `lineTypes`·`lineWeights`, `textStyles`, `dimensionStyles`를 주면 각 필드가 Dropdown이 됩니다(주지 않으면 기존 버튼). 모든 항목은 `id` 기반 배열 Props이며 추가·삭제·이름/값 변경·재정렬에 소스 수정이 필요 없습니다. 선택·입력은 `onColorSelect` / `onColorModeChange` / `onColorInput` / `onColorPick`, `onLineTypeSelect` / `onLineWeightSelect` / `onLineWeightValueChange` / `onLineScaleChange`, `onTextStyleSelect`, `onDimensionStyleSelect`, 하단 `… 상세`는 `on*Detail`로 각 프로그램이 연결합니다(상세 팝업은 미제공). Dropdown 안 독립 버튼은 Hover 효과가 없고 목록 행·Input은 Figma Hover를 유지합니다([명세 §5.3](../../docs/sw/sw-header-design-spec.md#53-색상--선스타일--문자스타일--치수스타일-dropdown-2026-10-16), [사용 가이드 §7](../../docs/sw/sw-header-usage.md#색상--선문자치수스타일-dropdown-예-2026-10-16)).
|
||||
|
||||
```tsx
|
||||
<SWHeader
|
||||
documentTitle={fileName}
|
||||
color={color} colors={palette} userColors={userColors} colorMode={colorMode}
|
||||
onColorSelect={option => applyColor(option)} onColorModeChange={setColorMode} onColorInput={({ hex }) => applyHex(hex)}
|
||||
lineStyle={lineStyle} lineTypes={lineTypes} lineWeights={lineWeights}
|
||||
onLineTypeSelect={type => setLineStyle(s => ({ ...s, name: type.name }))}
|
||||
textStyle={textStyle} textStyles={textStyles} onTextStyleSelect={style => setTextStyle(style)}
|
||||
dimensionStyle={dimensionStyle} dimensionStyles={dimensionStyles} onDimensionStyleSelect={style => setDimensionStyle(style)}
|
||||
/>
|
||||
```
|
||||
- 반응형 More: 좁은 헤더에서 더보기의 필드를 고르면 그 필드가 첫 칸(레이어 자리)에 표시되고 Dropdown이 바로 아래 열립니다. 레이어는 더보기에서 다시 고를 수 있으며, 전환은 UI 상태라 데이터 Callback을 호출하지 않습니다([명세 §5.4](../../docs/sw/sw-header-design-spec.md#54-반응형-more--대표-필드-전환-2026-10-16)).
|
||||
- Text Stroke: Figma에 Stroke가 있는 텍스트만 검정 외곽선(14·16px 1.3px, 12px 1px) + 자기 색 0.3px를 표시합니다. 더보기·Tooltip·`+ New`/`Open`은 제외([명세 §6.1](../../docs/sw/sw-header-design-spec.md#61-text-stroke-2026-10-16)).
|
||||
- `SWHeaderInput`: 레이어 상세 등 상세 영역에서 쓰는 수정 가능 Input(Figma 26:11656, Default / Hover / 편집, caret `#E60403`). `value`(확정값) · `onValueChange`(임시값) · `onCommit`(Enter·Focus 이동, 유효 + 변경 시) · `onCancel`(Escape) · `valueType`(`text` / `number`) · `parse` · `readOnly` · `disabled`([명세 §5.5](../../docs/sw/sw-header-design-spec.md#55-레이어-상세-버튼--수정-가능한-input-2026-10-16)).
|
||||
- Peer: `react`, `react-dom` `^19.0.0`. 런타임 dependency는 없습니다.
|
||||
- 출력: `dist/index.js`(ESM, Figma SVG asset은 data URI로 포함, 임시 Figma URL 없음), `dist/*.d.ts`, `dist/styles.css`.
|
||||
- CSS는 자동 import되지 않습니다. `@hm-design/sw/styles.css`를 한 번 import합니다.
|
||||
- Registry에는 아직 게시하지 않았습니다. 설치·사용·Props는 [사용 가이드](../../docs/sw/sw-header-usage.md), 디자인 기준은 [디자인 명세](../../docs/sw/sw-header-design-spec.md)를 따릅니다.
|
||||
|
||||
```bash
|
||||
npm run build -w @hm-design/sw
|
||||
npm run typecheck -w @hm-design/sw
|
||||
```
|
||||
|
||||
## 추가 연결: 속성창·K 가이드 (2026-10-16)
|
||||
|
||||
- `SWHeaderPropertyPanel` 및 property/group/selection 타입을 공개합니다. `SWHeader.propertyPanel`에 외부 데이터를 전달하면 기존 속성창 버튼·More와 연결됩니다.
|
||||
- `propertyPanelOpen/onPropertyPanelOpenChange`로 Controlled 열림을 지원하며, 생략하면 내부 열림 상태를 사용합니다. 기존 `onPropertyPanelToggle`을 보존합니다.
|
||||
- Text/Number/Select/Color/Slider/Computed, id 기반 동적 항목, 검증·확정·취소·외부 변경·ReadOnly/Disabled·그룹 펼침을 지원합니다. CAD 계산·선택·객체 변경은 호스트가 담당합니다.
|
||||
- 상세 5개는 Figma 고정 크기와 Hover를 적용합니다. ByLayer/ByBlock은 정확히 하나가 표시되며, 기존 null 직접 색상 의미는 보존합니다. 레이어/선/문자/치수 목록은 Hover와 Selected 디자인이 같습니다.
|
||||
- K는 기존 버튼을 재사용하고 원본 이미지 Pan/Zoom을 반복합니다. 640px 미만에서는 기존 More 항목에서도 같은 가이드를 표시합니다. Reduced Motion은 정적 이미지이며 기존 창 이동 1400ms는 유지합니다.
|
||||
- 지정된 일반 text-stroke-width 선언·변수는 제거했고 검정 text-shadow와 상세 pill의 별도 Stroke는 보존했습니다.
|
||||
- 사용 예시와 API는 [가이드 추가 절](../../docs/sw/sw-header-usage.md#속성창k-가이드-추가-연결-2026-10-16), 디자인 값은 [명세 추가 절](../../docs/sw/sw-header-design-spec.md#추가-수정-기준--속성창k-가이드-2026-10-16)을 확인하세요.
|
||||
- 검증: 기존 17 + 신규 4 Story, [현재 실행 결과 및 NOT VERIFIED](../../docs/sw/sw-header-additional-verification-2026-10-16.md). 과거 로그는 보존합니다.
|
||||
|
||||
## 후속 5개 요구사항 API (2026-10-16)
|
||||
|
||||
- `SWHeaderPropertyBar` / `SWHeaderPropertyBarProps`를 공개합니다. `propertyBar`로 selection/properties/onPropertyChange와 readOnly/disabled를 전달합니다. 속성창과 SWHeaderProperty/Selection 데이터를 공유하며, `propertyBarOpen/defaultPropertyBarOpen/onPropertyBarOpenChange`는 별도의 표시 상태입니다. 최대 폭 1222·높이 28px이고 Header 49px 아래에 표시됩니다.
|
||||
- `SWHeaderControlId` / `SWHeaderControlConfig` / `SWHeader.controls`는 메뉴 자체의 전체 allowlist입니다. undefined는 기존 기본 구성, []는 선택적 컨트롤 없음입니다. id/visible/label과 배열 순서로 제거·복원·변경·재배치하며 More도 동일 구성을 따릅니다. 내부 옵션 배열·선택값 Props는 그대로입니다.
|
||||
- Text Stroke Story의 지정 원본 이미지 블록·전용 import를 제거했습니다. 원본 PNG와 다른 비교 Story는 보존합니다.
|
||||
- 상세 span의 `-webkit-text-stroke-width`와 미사용 `--hm-sw-header-solid-stroke-width`를 src/theme/sw.css에서 제거했습니다. build는 이 원본에서 dist/styles.css를 만드므로 dist만 편집하지 않습니다. 검정 text-shadow·SVG Stroke·상세 Hover는 보존합니다.
|
||||
- 색상 추출 버튼은 로컬 Figma Default/Hover SVG를 사용합니다(24px/18px). 기존 `onColorPick`은 그대로이며 Hover는 Callback을 실행하지 않습니다. `26:11338` 돋보기 Overlay는 호스트 연동 대상입니다.
|
||||
- Storybook은 기존 21개와 신규 Property Bar / Dynamic Controls / Colorize, 총 24개입니다. [최신 검증 기록](../../docs/sw/sw-header-followup-five-verification-2026-10-16.md)을 참조하세요.
|
||||
|
||||
|
||||
|
||||
## 3차 API (2026-10-16)
|
||||
|
||||
- `SWHeader.hiddenControls?: readonly SWHeaderHideableControlId[]` — `SWHeaderControlId` + `SWHeaderFrameControlId`(`home` / `windowMove` / `minimize` / `close`). 로고는 항상 표시하며 `home` 제거 시 정적 로고입니다. 제거한 요소는 공간·Popup·Callback이 없습니다.
|
||||
- `SWHeader.menuOrder?: readonly SWHeaderMenuId[]` — 5개 유사 메뉴끼리만 순서 변경. 지정 id 우선, 누락 id는 기본 순서로 뒤에, 중복·알 수 없는 id 무시. More 목록도 같은 순서입니다.
|
||||
- `SWHeaderPropertyPanelProps.shapeContent?: SWHeaderPropertyShapeContent` — `undefined` Figma 원형, `null`/`false` 빈 208×208 영역, ReactNode 또는 `(context: SWHeaderPropertyShapeContext) => ReactNode`. `SWHeader.propertyPanel`로도 전달됩니다.
|
||||
- 하위 공개 컴포넌트에 선택적 표시 Props 추가(기본 true): `SWHeaderHome.interactive`, `SWHeaderProperties.showCaption/showBar/showPanel`, `SWHeaderActions.showPrint/showPopupGuide`, `SWHeaderWindowControls.showWindowMove/showMinimize/showClose`.
|
||||
- Storybook: 기존 24개 + Composition, Property Panel 개별 4개, 4개 비교, 형상 Slot = 31개.
|
||||
@@ -0,0 +1,57 @@
|
||||
// Self-contained library build for @hm-design/sw (no shared script, no @hm-design/ui).
|
||||
import { readFile, readdir, rm, writeFile } from "node:fs/promises"
|
||||
import { existsSync } from "node:fs"
|
||||
import path from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { execFileSync } from "node:child_process"
|
||||
import { build } from "vite"
|
||||
|
||||
const packageRoot = fileURLToPath(new URL("./", import.meta.url))
|
||||
const output = path.join(packageRoot, "dist")
|
||||
const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"))
|
||||
const externals = Object.keys({ ...manifest.dependencies, ...manifest.peerDependencies })
|
||||
|
||||
await rm(output, { recursive: true, force: true })
|
||||
await build({
|
||||
configFile: false,
|
||||
root: packageRoot,
|
||||
publicDir: false,
|
||||
build: {
|
||||
target: "es2022",
|
||||
outDir: output,
|
||||
minify: false,
|
||||
// Figma SVG assets are inlined as data URIs: the package needs no asset path resolution.
|
||||
assetsInlineLimit: Number.MAX_SAFE_INTEGER,
|
||||
lib: {
|
||||
entry: { index: path.join(packageRoot, "src/index.ts") },
|
||||
formats: ["es"],
|
||||
fileName: (_format, entry) => `${entry}.js`,
|
||||
cssFileName: "styles",
|
||||
},
|
||||
rolldownOptions: {
|
||||
external: id => externals.some(dep => id === dep || id.startsWith(`${dep}/`)),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
execFileSync(process.execPath, [path.join(packageRoot, "../../node_modules/typescript/bin/tsc"), "-p", path.join(packageRoot, "tsconfig.build.json")], { stdio: "inherit" })
|
||||
|
||||
// Declarations describe APIs, not stylesheet loading; explicit .js specifiers also support NodeNext.
|
||||
async function finishDeclarations(directory) {
|
||||
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
||||
const file = path.join(directory, entry.name)
|
||||
if (entry.isDirectory()) await finishDeclarations(file)
|
||||
else if (entry.name.endsWith(".d.ts")) {
|
||||
const text = (await readFile(file, "utf8"))
|
||||
.replace(/^import\s+["'][^"']+\.css["'];?\s*$/gm, "")
|
||||
.replace(/(from\s+["']|import\(["'])(\.\.?\/[^"']+)(["'])/g, (_match, before, specifier, after) => {
|
||||
const target = path.resolve(directory, specifier)
|
||||
const resolved = existsSync(`${target}.d.ts`) ? specifier
|
||||
: existsSync(path.join(target, "index.d.ts")) ? `${specifier}/index` : specifier
|
||||
return `${before}${resolved.endsWith(".js") ? resolved : `${resolved}.js`}${after}`
|
||||
})
|
||||
await writeFile(file, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
await finishDeclarations(output)
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@hm-design/sw",
|
||||
"version": "0.1.0",
|
||||
"private": false,
|
||||
"description": "HM Design System SW (EasyBeam-style desktop software) components. Independent of @hm-design/ui.",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./styles.css": "./dist/styles.css"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json",
|
||||
"build": "node build.mjs",
|
||||
"prepack": "npm run build"
|
||||
},
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
declare module "*.svg" {
|
||||
const url: string
|
||||
export default url
|
||||
}
|
||||
declare module "*.png" {
|
||||
const url: string
|
||||
export default url
|
||||
}
|
||||
declare module "*.css"
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="32" height="48" viewBox="0 0 32 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="header/btn_종료">
|
||||
<rect width="32" height="48" fill="#E40000"/>
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M16 22.5854L20.5 18.0854L21.9142 19.4996L17.4142 23.9996L21.915 28.5004L20.5008 29.9146L16 25.4138L11.4992 29.9146L10.085 28.5004L14.5858 23.9996L10.0858 19.4996L11.5 18.0854L16 22.5854Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 544 B |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="32" height="48" viewBox="0 0 32 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="header/btn_종료">
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M16 22.5854L20.5 18.0854L21.9142 19.4996L17.4142 23.9996L21.915 28.5004L20.5008 29.9146L16 25.4138L11.4992 29.9146L10.085 28.5004L14.5858 23.9996L10.0858 19.4996L11.5 18.0854L16 22.5854Z" fill="#ADB0AF"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 500 B |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="colorize" opacity="0.7">
|
||||
<path id="Vector" d="M13.245 4.0575L13.935 4.7475L11.9175 6.765L11.2275 6.075L13.245 4.0575ZM13.2525 2.25C13.0575 2.25 12.87 2.325 12.72 2.4675L10.38 4.8075L8.9325 3.375L7.875 4.4325L8.94 5.4975L2.25 12.1875V15.75H5.8125L12.5025 9.06L13.5675 10.125L14.625 9.0675L13.185 7.6275L15.525 5.2875C15.825 4.9875 15.825 4.515 15.5325 4.2225L13.7775 2.4675C13.6275 2.325 13.44 2.25 13.2525 2.25ZM8.19 11.25L6.75 9.81L9.795 6.765L11.235 8.205L8.19 11.25Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 671 B |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="colorize" opacity="0.7">
|
||||
<path id="Vector" d="M13.245 4.0575L13.935 4.7475L11.9175 6.765L11.2275 6.075L13.245 4.0575ZM13.2525 2.25C13.0575 2.25 12.87 2.325 12.72 2.4675L10.38 4.8075L8.9325 3.375L7.875 4.4325L8.94 5.4975L2.25 12.1875V15.75H5.8125L12.5025 9.06L13.5675 10.125L14.625 9.0675L13.185 7.6275L15.525 5.2875C15.825 4.9875 15.825 4.515 15.5325 4.2225L13.7775 2.4675C13.6275 2.325 13.44 2.25 13.2525 2.25ZM8.19 11.25L6.75 9.81L9.795 6.765L11.235 8.205L8.19 11.25Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 671 B |
@@ -0,0 +1,8 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="colorize">
|
||||
<g id="Vector">
|
||||
<path d="M13.245 4.0575L13.935 4.7475L11.9175 6.765L11.2275 6.075L13.245 4.0575ZM13.2525 2.25C13.0575 2.25 12.87 2.325 12.72 2.4675L10.38 4.8075L8.9325 3.375L7.875 4.4325L8.94 5.4975L2.25 12.1875V15.75H5.8125L12.5025 9.06L13.5675 10.125L14.625 9.0675L13.185 7.6275L15.525 5.2875C15.825 4.9875 15.825 4.515 15.5325 4.2225L13.7775 2.4675C13.6275 2.325 13.44 2.25 13.2525 2.25ZM8.19 11.25L6.75 9.81L9.795 6.765L11.235 8.205L8.19 11.25Z" fill="#FF5C00"/>
|
||||
<path d="M13.2529 1.75C13.5348 1.7501 13.8066 1.8486 14.0293 2.02441L14.1221 2.10547L15.8857 3.86914C16.3766 4.36002 16.3705 5.14859 15.8789 5.64062L13.8916 7.62695L15.332 9.06738L13.5674 10.832L12.502 9.7666L6.16602 16.1035L6.01953 16.25H1.75V11.9805L1.89648 11.834L8.23242 5.49707L7.16797 4.43262L8.93066 2.66992L9.28418 3.01953L10.3779 4.10156L12.3662 2.11426L12.376 2.10449V2.10547C12.6158 1.87768 12.9255 1.75 13.2529 1.75ZM7.45703 9.80957L8.19043 10.543L10.5283 8.20508L9.79492 7.47168L7.45703 9.80957Z" stroke="black" stroke-opacity="0.4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="37" height="19" viewBox="0 0 37 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_dualmonitor" filter="url(#filter0_d_0_121)">
|
||||
<g id="Union">
|
||||
<mask id="path-1-inside-1_0_121" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 14V13H18V4H4V13H10V14H8V15H10H12H14V14H12Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 14V13H18V4H4V13H10V14H8V15H10H12H14V14H12Z" fill="white"/>
|
||||
<path d="M12 13V12H11V13H12ZM12 14H11V15H12V14ZM18 13V14H19V13H18ZM18 4H19V3H18V4ZM4 4V3H3V4H4ZM4 13H3V14H4V13ZM10 13H11V12H10V13ZM10 14V15H11V14H10ZM8 14V13H7V14H8ZM8 15H7V16H8V15ZM14 15V16H15V15H14ZM14 14H15V13H14V14ZM12 13H11V14H12H13V13H12ZM18 13V12H12V13V14H18V13ZM18 4H17V13H18H19V4H18ZM4 4V5H18V4V3H4V4ZM4 13H5V4H4H3V13H4ZM10 13V12H4V13V14H10V13ZM10 14H11V13H10H9V14H10ZM8 14V15H10V14V13H8V14ZM8 15H9V14H8H7V15H8ZM10 15V14H8V15V16H10V15ZM12 15V14H10V15V16H12V15ZM14 15V14H12V15V16H14V15ZM14 14H13V15H14H15V14H14ZM12 14V15H14V14V13H12V14Z" fill="#4B7165" mask="url(#path-1-inside-1_0_121)"/>
|
||||
</g>
|
||||
<g id="Union_2">
|
||||
<mask id="path-3-inside-2_0_121" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M27 14V13H33V4H19V13H25V14H23V15H25H27H29V14H27Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M27 14V13H33V4H19V13H25V14H23V15H25H27H29V14H27Z" fill="#FFE500"/>
|
||||
<path d="M27 13V12H26V13H27ZM27 14H26V15H27V14ZM33 13V14H34V13H33ZM33 4H34V3H33V4ZM19 4V3H18V4H19ZM19 13H18V14H19V13ZM25 13H26V12H25V13ZM25 14V15H26V14H25ZM23 14V13H22V14H23ZM23 15H22V16H23V15ZM29 15V16H30V15H29ZM29 14H30V13H29V14ZM27 13H26V14H27H28V13H27ZM33 13V12H27V13V14H33V13ZM33 4H32V13H33H34V4H33ZM19 4V5H33V4V3H19V4ZM19 13H20V4H19H18V13H19ZM25 13V12H19V13V14H25V13ZM25 14H26V13H25H24V14H25ZM23 14V15H25V14V13H23V14ZM23 15H24V14H23H22V15H23ZM25 15V14H23V15V16H25V15ZM27 15V14H25V15V16H27V15ZM29 15V14H27V15V16H29V15ZM29 14H28V15H29H30V14H29ZM27 14V15H29V14V13H27V14Z" fill="#264F42" mask="url(#path-3-inside-2_0_121)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_121" x="0" y="-1" width="37" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.6 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_121"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_121" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="6.06218" height="3" viewBox="0 0 6.06218 3" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="Polygon 352" d="M3.03109 0L6.06218 3H0L3.03109 0Z" fill="#47AF91"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 259 B |
@@ -0,0 +1,32 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="92" height="72" viewBox="0 0 92 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Rectangle 7508" filter="url(#filter0_d_0_10)">
|
||||
<path d="M8 12H76L61.8234 54.5298C60.7345 57.7966 57.6774 60 54.2339 60H8V12Z" fill="url(#paint0_linear_0_10)"/>
|
||||
<path d="M75.3057 12.5L61.3486 54.3721C60.3277 57.4342 57.4623 59.4998 54.2344 59.5H8.5V12.5H75.3057Z" stroke="url(#paint1_linear_0_10)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_10" x="0" y="0" width="92" height="72" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="4"/>
|
||||
<feGaussianBlur stdDeviation="6"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0"/>
|
||||
<feBlend mode="multiply" in2="BackgroundImageFix" result="effect1_dropShadow_0_10"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_10" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_10" x1="8" y1="60.0004" x2="8" y2="12" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1F3F36"/>
|
||||
<stop offset="0.09" stop-color="#0A2C22"/>
|
||||
<stop offset="0.15" stop-color="#0D392C"/>
|
||||
<stop offset="0.88" stop-color="#0E3F31"/>
|
||||
<stop offset="0.97" stop-color="#0A2C22"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_0_10" x1="42" y1="12" x2="42" y2="60" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#A67D03"/>
|
||||
<stop offset="0.164109" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.371922" stop-color="#FADD4F"/>
|
||||
<stop offset="0.620285" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.878785" stop-color="#926E03"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,31 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="92" height="72" viewBox="0 0 92 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Rectangle 7508" filter="url(#filter0_d_0_2957)">
|
||||
<path d="M8 12H76L61.8234 54.5298C60.7345 57.7966 57.6774 60 54.2339 60H8V12Z" fill="url(#paint0_linear_0_2957)"/>
|
||||
<path d="M75.3057 12.5L61.3486 54.3721C60.3277 57.4342 57.4623 59.4998 54.2344 59.5H8.5V12.5H75.3057Z" stroke="url(#paint1_linear_0_2957)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_2957" x="0" y="0" width="92" height="72" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="4"/>
|
||||
<feGaussianBlur stdDeviation="6"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0"/>
|
||||
<feBlend mode="multiply" in2="BackgroundImageFix" result="effect1_dropShadow_0_2957"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_2957" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_2957" x1="8" y1="60.0004" x2="8" y2="12" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1F3F36"/>
|
||||
<stop offset="0.09" stop-color="#0A2C22"/>
|
||||
<stop offset="0.15" stop-color="#0D392C"/>
|
||||
<stop offset="0.88" stop-color="#0E3F31"/>
|
||||
<stop offset="0.97" stop-color="#0A2C22"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_0_2957" x1="42" y1="12" x2="42" y2="60" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#08251C"/>
|
||||
<stop offset="0.370561" stop-color="#208769"/>
|
||||
<stop offset="0.805" stop-color="#08241C"/>
|
||||
<stop offset="1" stop-color="#051612"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="192" height="109" viewBox="0 0 192 109" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 7349" width="192" height="109" fill="#D9D9D9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 245 B |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="192" height="109" viewBox="0 0 192 109" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 7349" x="0.5" y="0.5" width="191" height="108" fill="black" stroke="url(#paint0_linear_0_14)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_14" x1="96" y1="0" x2="96" y2="109" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#A67D03"/>
|
||||
<stop offset="0.164109" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.371922" stop-color="#FADD4F"/>
|
||||
<stop offset="0.620285" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.878785" stop-color="#926E03"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 647 B |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="192" height="109" viewBox="0 0 192 109" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 7349" x="0.5" y="0.5" width="191" height="108" fill="#D9D9D9" stroke="url(#paint0_linear_0_12)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_12" x1="96" y1="0" x2="96" y2="109" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#A67D03"/>
|
||||
<stop offset="0.164109" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.371922" stop-color="#FADD4F"/>
|
||||
<stop offset="0.620285" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.878785" stop-color="#926E03"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 649 B |
|
After Width: | Height: | Size: 276 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="ico_layer">
|
||||
<path id="Union" opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M1 1H11V3H13V10V13H10H3V11H1V1ZM3 10H10V3V2H2V10H3Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 340 B |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="ico_layer">
|
||||
<path id="Union" opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M1 1H11V3H13V10V13H10H3V11H1V1ZM3 10H10V3V2H2V10H3Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 339 B |
@@ -0,0 +1,13 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Ellipse 419">
|
||||
<circle cx="7" cy="7" r="6" fill="url(#paint0_radial_0_5)"/>
|
||||
<circle cx="7" cy="7" r="6.5" stroke="black" stroke-opacity="0.5"/>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_0_5" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7 5.28571) rotate(90) scale(8.14286)">
|
||||
<stop stop-color="#E6E6E6"/>
|
||||
<stop offset="0.440437" stop-color="#9C9C9C"/>
|
||||
<stop offset="1" stop-color="#555555"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 635 B |
@@ -0,0 +1,13 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Ellipse 419">
|
||||
<circle cx="7" cy="7" r="6" fill="url(#paint0_radial_0_2958)"/>
|
||||
<circle cx="7" cy="7" r="6.5" stroke="black" stroke-opacity="0.5"/>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_0_2958" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7 5.28571) rotate(90) scale(8.14286)">
|
||||
<stop stop-color="#FFE6BC"/>
|
||||
<stop offset="0.440437" stop-color="#FAB846"/>
|
||||
<stop offset="1" stop-color="#D38600"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 641 B |
@@ -0,0 +1,13 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_눈꽃">
|
||||
<rect width="20" height="20" rx="2" fill="black" fill-opacity="0.25"/>
|
||||
<g id="icon_눈꽃_2">
|
||||
<mask id="path-2-outside-1_0_74" maskUnits="userSpaceOnUse" x="1.5" y="1.5" width="17" height="17" fill="black">
|
||||
<rect fill="white" x="1.5" y="1.5" width="17" height="17"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z" fill="#00CCFF"/>
|
||||
<path d="M9.5 4.79282L8.79289 5.49992C9.07889 5.78592 9.50901 5.87148 9.88268 5.7167C10.2564 5.56192 10.5 5.19728 10.5 4.79282H9.5ZM8.56066 3.85348L9.26777 3.14637L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L7.14645 3.14637L7.14645 3.14637L7.85355 3.85348ZM7.85355 4.56058L7.14645 5.26769L7.14645 5.26769L7.85355 4.56058ZM9.26777 5.9748L9.97487 5.26769V5.26769L9.26777 5.9748ZM9.5 6.10638H10.5C10.5 5.64719 10.1873 5.24701 9.74168 5.13603L9.5 6.10638ZM9.5 8.78906L8.79289 9.49617C9.07889 9.78217 9.50901 9.86772 9.88268 9.71294C10.2564 9.55816 10.5 9.19352 10.5 8.78906H9.5ZM5.70113 6.40441L6.40824 5.6973V5.6973L5.70113 6.40441ZM8.79672 9.5V10.5C9.20119 10.5 9.56582 10.2564 9.7206 9.88268C9.87538 9.50901 9.78983 9.07889 9.50383 8.79289L8.79672 9.5ZM6.10247 9.5L5.13212 9.74168C5.2431 10.1873 5.64328 10.5 6.10247 10.5V9.5ZM5.97089 9.26777L5.26378 9.97487V9.97487L5.97089 9.26777ZM4.55668 7.85355L5.26378 7.14645L5.26378 7.14645L4.55668 7.85355ZM3.84957 7.85355L3.14246 7.14645L3.14246 7.14645L3.84957 7.85355ZM3.84957 8.56066L3.14246 9.26777L3.14246 9.26777L3.84957 8.56066ZM4.78891 9.5V10.5C5.19337 10.5 5.55801 10.2564 5.71279 9.88268C5.86757 9.50901 5.78202 9.07889 5.49602 8.79289L4.78891 9.5ZM4.61704 10.5L5.32414 11.2071C5.61014 10.9211 5.6957 10.491 5.54092 10.1173C5.38614 9.74364 5.0215 9.5 4.61704 9.5V10.5ZM3.84957 11.2675L4.55668 11.9746L4.55668 11.9746L3.84957 11.2675ZM3.84957 11.9746L4.55668 11.2675L4.55668 11.2675L3.84957 11.9746ZM4.55668 11.9746L3.84957 11.2675L3.84957 11.2675L4.55668 11.9746ZM5.97089 10.5604L6.678 11.2675L6.678 11.2675L5.97089 10.5604ZM6.02244 10.5V9.5C5.70212 9.5 5.40119 9.65345 5.21307 9.91271L6.02244 10.5ZM8.79688 10.5L9.50398 11.2071C9.78998 10.9211 9.87554 10.491 9.72076 10.1173C9.56597 9.74364 9.20134 9.5 8.79688 9.5V10.5ZM5.11429 14.1826L5.8214 14.8897L5.8214 14.8897L5.11429 14.1826ZM5.11429 14.8897L5.8214 14.1826L5.8214 14.1826L5.11429 14.8897ZM9.5 11.2111H10.5C10.5 10.8066 10.2564 10.442 9.88268 10.2872C9.50901 10.1324 9.07889 10.218 8.79289 10.504L9.5 11.2111ZM9.5 13.7217L9.74168 14.6921C10.1873 14.5811 10.5 14.1809 10.5 13.7217H9.5ZM9.26777 13.8533L8.56066 13.1462L8.56066 13.1462L9.26777 13.8533ZM7.85355 15.2675L7.14645 14.5604L7.14644 14.5604L7.85355 15.2675ZM7.85355 15.9746L7.14644 16.6818L7.14645 16.6818L7.85355 15.9746ZM8.56066 15.9746L7.85355 15.2675L7.85355 15.2675L8.56066 15.9746ZM9.5 15.0353H10.5C10.5 14.6308 10.2564 14.2662 9.88268 14.1114C9.50901 13.9566 9.07889 14.0422 8.79289 14.3282L9.5 15.0353ZM10.5 15.2072L11.2071 14.5001C10.9211 14.2141 10.491 14.1285 10.1173 14.2833C9.74364 14.4381 9.5 14.8027 9.5 15.2072H10.5ZM11.2675 15.9746L11.9746 15.2675L11.9746 15.2675L11.2675 15.9746ZM10.5 13.8018H9.5C9.5 14.1221 9.65345 14.423 9.91271 14.6111L10.5 13.8018ZM10.5 11.2033L11.2071 10.4962C10.9211 10.2102 10.491 10.1246 10.1173 10.2794C9.74364 10.4342 9.5 10.7988 9.5 11.2033H10.5ZM14.1864 14.8897L13.4793 15.5968L13.4793 15.5968L14.1864 14.8897ZM11.2109 10.5V9.5C10.8065 9.5 10.4418 9.74364 10.2871 10.1173C10.1323 10.491 10.2178 10.9211 10.5038 11.2071L11.2109 10.5ZM13.8057 10.5L14.615 9.9127C14.4269 9.65344 14.126 9.5 13.8057 9.5V10.5ZM15.9786 11.2675L16.6857 10.5604V10.5604L15.9786 11.2675ZM15.2111 10.5V9.5C14.8066 9.5 14.442 9.74364 14.2872 10.1173C14.1324 10.491 14.218 10.9211 14.504 11.2071L15.2111 10.5ZM15.0392 9.5L14.3321 8.79289C14.0461 9.07889 13.9606 9.50901 14.1153 9.88268C14.2701 10.2564 14.6348 10.5 15.0392 10.5V9.5ZM15.9786 8.56066L15.2714 7.85355L15.2714 7.85355L15.9786 8.56066ZM15.9786 7.85355L16.6857 7.14645L16.6857 7.14644L15.9786 7.85355ZM15.2714 7.85355L14.5643 7.14644L14.5643 7.14645L15.2714 7.85355ZM13.8572 9.26777L14.5643 9.97488L14.5643 9.97487L13.8572 9.26777ZM13.7257 9.5V10.5C14.1848 10.5 14.585 10.1873 14.696 9.74168L13.7257 9.5ZM11.2111 9.5L10.504 8.79289C10.218 9.07889 10.1324 9.50901 10.2872 9.88268C10.442 10.2564 10.8066 10.5 11.2111 10.5V9.5ZM14.3067 6.40441L15.0138 7.11152L15.0138 7.11152L14.3067 6.40441ZM14.3067 5.6973L15.0138 4.9902L15.0138 4.99019L14.3067 5.6973ZM13.5996 5.6973L12.8925 4.9902V4.9902L13.5996 5.6973ZM10.5 8.79688H9.5C9.5 9.20134 9.74364 9.56597 10.1173 9.72076C10.491 9.87554 10.9211 9.78998 11.2071 9.50398L10.5 8.79688ZM10.5 6.02635L9.91271 5.21698C9.65345 5.4051 9.5 5.70603 9.5 6.02635H10.5ZM10.5604 5.9748L11.2675 6.68191L11.2675 6.6819L10.5604 5.9748ZM11.9746 4.56058L11.2675 3.85348L11.2675 3.85348L11.9746 4.56058ZM11.9746 3.85348L11.2675 4.56058L11.2675 4.56059L11.9746 3.85348ZM11.2675 3.85348L11.9746 4.56058L11.9746 4.56058L11.2675 3.85348ZM10.5 4.62094H9.5C9.5 5.02541 9.74364 5.39004 10.1173 5.54482C10.491 5.6996 10.9211 5.61405 11.2071 5.32805L10.5 4.62094ZM9.5 3H10.5C10.5 3.27614 10.2761 3.5 10 3.5V2.5V1.5C9.17157 1.5 8.5 2.17157 8.5 3H9.5ZM9.5 4.79282H10.5V3H9.5H8.5V4.79282H9.5ZM8.56066 3.85348L7.85355 4.56058L8.79289 5.49992L9.5 4.79282L10.2071 4.08571L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L8.56066 4.56059C8.3654 4.75585 8.04882 4.75585 7.85355 4.56059L8.56066 3.85348L9.26777 3.14637C8.68198 2.56058 7.7322Line truncated
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_눈꽃">
|
||||
<rect width="20" height="20" rx="2" fill="black" fill-opacity="0.25"/>
|
||||
<g id="icon_눈꽃_2">
|
||||
<mask id="path-2-outside-1_0_18" maskUnits="userSpaceOnUse" x="1.5" y="1.5" width="17" height="17" fill="black">
|
||||
<rect fill="white" x="1.5" y="1.5" width="17" height="17"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z" fill="#949494"/>
|
||||
<path d="M9.5 4.79282L8.79289 5.49992C9.07889 5.78592 9.50901 5.87148 9.88268 5.7167C10.2564 5.56192 10.5 5.19728 10.5 4.79282H9.5ZM8.56066 3.85348L9.26777 3.14637L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L7.14645 3.14637L7.14645 3.14637L7.85355 3.85348ZM7.85355 4.56058L7.14645 5.26769L7.14645 5.26769L7.85355 4.56058ZM9.26777 5.9748L9.97487 5.26769V5.26769L9.26777 5.9748ZM9.5 6.10638H10.5C10.5 5.64719 10.1873 5.24701 9.74168 5.13603L9.5 6.10638ZM9.5 8.78906L8.79289 9.49617C9.07889 9.78217 9.50901 9.86772 9.88268 9.71294C10.2564 9.55816 10.5 9.19352 10.5 8.78906H9.5ZM5.70113 6.40441L6.40824 5.6973V5.6973L5.70113 6.40441ZM8.79672 9.5V10.5C9.20119 10.5 9.56582 10.2564 9.7206 9.88268C9.87538 9.50901 9.78983 9.07889 9.50383 8.79289L8.79672 9.5ZM6.10247 9.5L5.13212 9.74168C5.2431 10.1873 5.64328 10.5 6.10247 10.5V9.5ZM5.97089 9.26777L5.26378 9.97487V9.97487L5.97089 9.26777ZM4.55668 7.85355L5.26378 7.14645L5.26378 7.14645L4.55668 7.85355ZM3.84957 7.85355L3.14246 7.14645L3.14246 7.14645L3.84957 7.85355ZM3.84957 8.56066L3.14246 9.26777L3.14246 9.26777L3.84957 8.56066ZM4.78891 9.5V10.5C5.19337 10.5 5.55801 10.2564 5.71279 9.88268C5.86757 9.50901 5.78202 9.07889 5.49602 8.79289L4.78891 9.5ZM4.61704 10.5L5.32414 11.2071C5.61014 10.9211 5.6957 10.491 5.54092 10.1173C5.38614 9.74364 5.0215 9.5 4.61704 9.5V10.5ZM3.84957 11.2675L4.55668 11.9746L4.55668 11.9746L3.84957 11.2675ZM3.84957 11.9746L4.55668 11.2675L4.55668 11.2675L3.84957 11.9746ZM4.55668 11.9746L3.84957 11.2675L3.84957 11.2675L4.55668 11.9746ZM5.97089 10.5604L6.678 11.2675L6.678 11.2675L5.97089 10.5604ZM6.02244 10.5V9.5C5.70212 9.5 5.40119 9.65345 5.21307 9.91271L6.02244 10.5ZM8.79688 10.5L9.50398 11.2071C9.78998 10.9211 9.87554 10.491 9.72076 10.1173C9.56597 9.74364 9.20134 9.5 8.79688 9.5V10.5ZM5.11429 14.1826L5.8214 14.8897L5.8214 14.8897L5.11429 14.1826ZM5.11429 14.8897L5.8214 14.1826L5.8214 14.1826L5.11429 14.8897ZM9.5 11.2111H10.5C10.5 10.8066 10.2564 10.442 9.88268 10.2872C9.50901 10.1324 9.07889 10.218 8.79289 10.504L9.5 11.2111ZM9.5 13.7217L9.74168 14.6921C10.1873 14.5811 10.5 14.1809 10.5 13.7217H9.5ZM9.26777 13.8533L8.56066 13.1462L8.56066 13.1462L9.26777 13.8533ZM7.85355 15.2675L7.14645 14.5604L7.14644 14.5604L7.85355 15.2675ZM7.85355 15.9746L7.14644 16.6818L7.14645 16.6818L7.85355 15.9746ZM8.56066 15.9746L7.85355 15.2675L7.85355 15.2675L8.56066 15.9746ZM9.5 15.0353H10.5C10.5 14.6308 10.2564 14.2662 9.88268 14.1114C9.50901 13.9566 9.07889 14.0422 8.79289 14.3282L9.5 15.0353ZM10.5 15.2072L11.2071 14.5001C10.9211 14.2141 10.491 14.1285 10.1173 14.2833C9.74364 14.4381 9.5 14.8027 9.5 15.2072H10.5ZM11.2675 15.9746L11.9746 15.2675L11.9746 15.2675L11.2675 15.9746ZM10.5 13.8018H9.5C9.5 14.1221 9.65345 14.423 9.91271 14.6111L10.5 13.8018ZM10.5 11.2033L11.2071 10.4962C10.9211 10.2102 10.491 10.1246 10.1173 10.2794C9.74364 10.4342 9.5 10.7988 9.5 11.2033H10.5ZM14.1864 14.8897L13.4793 15.5968L13.4793 15.5968L14.1864 14.8897ZM11.2109 10.5V9.5C10.8065 9.5 10.4418 9.74364 10.2871 10.1173C10.1323 10.491 10.2178 10.9211 10.5038 11.2071L11.2109 10.5ZM13.8057 10.5L14.615 9.9127C14.4269 9.65344 14.126 9.5 13.8057 9.5V10.5ZM15.9786 11.2675L16.6857 10.5604V10.5604L15.9786 11.2675ZM15.2111 10.5V9.5C14.8066 9.5 14.442 9.74364 14.2872 10.1173C14.1324 10.491 14.218 10.9211 14.504 11.2071L15.2111 10.5ZM15.0392 9.5L14.3321 8.79289C14.0461 9.07889 13.9606 9.50901 14.1153 9.88268C14.2701 10.2564 14.6348 10.5 15.0392 10.5V9.5ZM15.9786 8.56066L15.2714 7.85355L15.2714 7.85355L15.9786 8.56066ZM15.9786 7.85355L16.6857 7.14645L16.6857 7.14644L15.9786 7.85355ZM15.2714 7.85355L14.5643 7.14644L14.5643 7.14645L15.2714 7.85355ZM13.8572 9.26777L14.5643 9.97488L14.5643 9.97487L13.8572 9.26777ZM13.7257 9.5V10.5C14.1848 10.5 14.585 10.1873 14.696 9.74168L13.7257 9.5ZM11.2111 9.5L10.504 8.79289C10.218 9.07889 10.1324 9.50901 10.2872 9.88268C10.442 10.2564 10.8066 10.5 11.2111 10.5V9.5ZM14.3067 6.40441L15.0138 7.11152L15.0138 7.11152L14.3067 6.40441ZM14.3067 5.6973L15.0138 4.9902L15.0138 4.99019L14.3067 5.6973ZM13.5996 5.6973L12.8925 4.9902V4.9902L13.5996 5.6973ZM10.5 8.79688H9.5C9.5 9.20134 9.74364 9.56597 10.1173 9.72076C10.491 9.87554 10.9211 9.78998 11.2071 9.50398L10.5 8.79688ZM10.5 6.02635L9.91271 5.21698C9.65345 5.4051 9.5 5.70603 9.5 6.02635H10.5ZM10.5604 5.9748L11.2675 6.68191L11.2675 6.6819L10.5604 5.9748ZM11.9746 4.56058L11.2675 3.85348L11.2675 3.85348L11.9746 4.56058ZM11.9746 3.85348L11.2675 4.56058L11.2675 4.56059L11.9746 3.85348ZM11.2675 3.85348L11.9746 4.56058L11.9746 4.56058L11.2675 3.85348ZM10.5 4.62094H9.5C9.5 5.02541 9.74364 5.39004 10.1173 5.54482C10.491 5.6996 10.9211 5.61405 11.2071 5.32805L10.5 4.62094ZM9.5 3H10.5C10.5 3.27614 10.2761 3.5 10 3.5V2.5V1.5C9.17157 1.5 8.5 2.17157 8.5 3H9.5ZM9.5 4.79282H10.5V3H9.5H8.5V4.79282H9.5ZM8.56066 3.85348L7.85355 4.56058L8.79289 5.49992L9.5 4.79282L10.2071 4.08571L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L8.56066 4.56059C8.3654 4.75585 8.04882 4.75585 7.85355 4.56059L8.56066 3.85348L9.26777 3.14637C8.68198 2.56058 7.7322Line truncated
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_눈꽃">
|
||||
<g id="icon_눈꽃_2">
|
||||
<mask id="path-2-outside-1_0_46" maskUnits="userSpaceOnUse" x="1.5" y="1.5" width="17" height="17" fill="black">
|
||||
<rect fill="white" x="1.5" y="1.5" width="17" height="17"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z" fill="#949494"/>
|
||||
<path d="M9.5 4.79282L8.79289 5.49992C9.07889 5.78592 9.50901 5.87148 9.88268 5.7167C10.2564 5.56192 10.5 5.19728 10.5 4.79282H9.5ZM8.56066 3.85348L9.26777 3.14637L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L7.14645 3.14637L7.14645 3.14637L7.85355 3.85348ZM7.85355 4.56058L7.14645 5.26769L7.14645 5.26769L7.85355 4.56058ZM9.26777 5.9748L9.97487 5.26769V5.26769L9.26777 5.9748ZM9.5 6.10638H10.5C10.5 5.64719 10.1873 5.24701 9.74168 5.13603L9.5 6.10638ZM9.5 8.78906L8.79289 9.49617C9.07889 9.78217 9.50901 9.86772 9.88268 9.71294C10.2564 9.55816 10.5 9.19352 10.5 8.78906H9.5ZM5.70113 6.40441L6.40824 5.6973V5.6973L5.70113 6.40441ZM8.79672 9.5V10.5C9.20119 10.5 9.56582 10.2564 9.7206 9.88268C9.87538 9.50901 9.78983 9.07889 9.50383 8.79289L8.79672 9.5ZM6.10247 9.5L5.13212 9.74168C5.2431 10.1873 5.64328 10.5 6.10247 10.5V9.5ZM5.97089 9.26777L5.26378 9.97487V9.97487L5.97089 9.26777ZM4.55668 7.85355L5.26378 7.14645L5.26378 7.14645L4.55668 7.85355ZM3.84957 7.85355L3.14246 7.14645L3.14246 7.14645L3.84957 7.85355ZM3.84957 8.56066L3.14246 9.26777L3.14246 9.26777L3.84957 8.56066ZM4.78891 9.5V10.5C5.19337 10.5 5.55801 10.2564 5.71279 9.88268C5.86757 9.50901 5.78202 9.07889 5.49602 8.79289L4.78891 9.5ZM4.61704 10.5L5.32414 11.2071C5.61014 10.9211 5.6957 10.491 5.54092 10.1173C5.38614 9.74364 5.0215 9.5 4.61704 9.5V10.5ZM3.84957 11.2675L4.55668 11.9746L4.55668 11.9746L3.84957 11.2675ZM3.84957 11.9746L4.55668 11.2675L4.55668 11.2675L3.84957 11.9746ZM4.55668 11.9746L3.84957 11.2675L3.84957 11.2675L4.55668 11.9746ZM5.97089 10.5604L6.678 11.2675L6.678 11.2675L5.97089 10.5604ZM6.02244 10.5V9.5C5.70212 9.5 5.40119 9.65345 5.21307 9.91271L6.02244 10.5ZM8.79688 10.5L9.50398 11.2071C9.78998 10.9211 9.87554 10.491 9.72076 10.1173C9.56597 9.74364 9.20134 9.5 8.79688 9.5V10.5ZM5.11429 14.1826L5.8214 14.8897L5.8214 14.8897L5.11429 14.1826ZM5.11429 14.8897L5.8214 14.1826L5.8214 14.1826L5.11429 14.8897ZM9.5 11.2111H10.5C10.5 10.8066 10.2564 10.442 9.88268 10.2872C9.50901 10.1324 9.07889 10.218 8.79289 10.504L9.5 11.2111ZM9.5 13.7217L9.74168 14.6921C10.1873 14.5811 10.5 14.1809 10.5 13.7217H9.5ZM9.26777 13.8533L8.56066 13.1462L8.56066 13.1462L9.26777 13.8533ZM7.85355 15.2675L7.14645 14.5604L7.14644 14.5604L7.85355 15.2675ZM7.85355 15.9746L7.14644 16.6818L7.14645 16.6818L7.85355 15.9746ZM8.56066 15.9746L7.85355 15.2675L7.85355 15.2675L8.56066 15.9746ZM9.5 15.0353H10.5C10.5 14.6308 10.2564 14.2662 9.88268 14.1114C9.50901 13.9566 9.07889 14.0422 8.79289 14.3282L9.5 15.0353ZM10.5 15.2072L11.2071 14.5001C10.9211 14.2141 10.491 14.1285 10.1173 14.2833C9.74364 14.4381 9.5 14.8027 9.5 15.2072H10.5ZM11.2675 15.9746L11.9746 15.2675L11.9746 15.2675L11.2675 15.9746ZM10.5 13.8018H9.5C9.5 14.1221 9.65345 14.423 9.91271 14.6111L10.5 13.8018ZM10.5 11.2033L11.2071 10.4962C10.9211 10.2102 10.491 10.1246 10.1173 10.2794C9.74364 10.4342 9.5 10.7988 9.5 11.2033H10.5ZM14.1864 14.8897L13.4793 15.5968L13.4793 15.5968L14.1864 14.8897ZM11.2109 10.5V9.5C10.8065 9.5 10.4418 9.74364 10.2871 10.1173C10.1323 10.491 10.2178 10.9211 10.5038 11.2071L11.2109 10.5ZM13.8057 10.5L14.615 9.9127C14.4269 9.65344 14.126 9.5 13.8057 9.5V10.5ZM15.9786 11.2675L16.6857 10.5604V10.5604L15.9786 11.2675ZM15.2111 10.5V9.5C14.8066 9.5 14.442 9.74364 14.2872 10.1173C14.1324 10.491 14.218 10.9211 14.504 11.2071L15.2111 10.5ZM15.0392 9.5L14.3321 8.79289C14.0461 9.07889 13.9606 9.50901 14.1153 9.88268C14.2701 10.2564 14.6348 10.5 15.0392 10.5V9.5ZM15.9786 8.56066L15.2714 7.85355L15.2714 7.85355L15.9786 8.56066ZM15.9786 7.85355L16.6857 7.14645L16.6857 7.14644L15.9786 7.85355ZM15.2714 7.85355L14.5643 7.14644L14.5643 7.14645L15.2714 7.85355ZM13.8572 9.26777L14.5643 9.97488L14.5643 9.97487L13.8572 9.26777ZM13.7257 9.5V10.5C14.1848 10.5 14.585 10.1873 14.696 9.74168L13.7257 9.5ZM11.2111 9.5L10.504 8.79289C10.218 9.07889 10.1324 9.50901 10.2872 9.88268C10.442 10.2564 10.8066 10.5 11.2111 10.5V9.5ZM14.3067 6.40441L15.0138 7.11152L15.0138 7.11152L14.3067 6.40441ZM14.3067 5.6973L15.0138 4.9902L15.0138 4.99019L14.3067 5.6973ZM13.5996 5.6973L12.8925 4.9902V4.9902L13.5996 5.6973ZM10.5 8.79688H9.5C9.5 9.20134 9.74364 9.56597 10.1173 9.72076C10.491 9.87554 10.9211 9.78998 11.2071 9.50398L10.5 8.79688ZM10.5 6.02635L9.91271 5.21698C9.65345 5.4051 9.5 5.70603 9.5 6.02635H10.5ZM10.5604 5.9748L11.2675 6.68191L11.2675 6.6819L10.5604 5.9748ZM11.9746 4.56058L11.2675 3.85348L11.2675 3.85348L11.9746 4.56058ZM11.9746 3.85348L11.2675 4.56058L11.2675 4.56059L11.9746 3.85348ZM11.2675 3.85348L11.9746 4.56058L11.9746 4.56058L11.2675 3.85348ZM10.5 4.62094H9.5C9.5 5.02541 9.74364 5.39004 10.1173 5.54482C10.491 5.6996 10.9211 5.61405 11.2071 5.32805L10.5 4.62094ZM9.5 3H10.5C10.5 3.27614 10.2761 3.5 10 3.5V2.5V1.5C9.17157 1.5 8.5 2.17157 8.5 3H9.5ZM9.5 4.79282H10.5V3H9.5H8.5V4.79282H9.5ZM8.56066 3.85348L7.85355 4.56058L8.79289 5.49992L9.5 4.79282L10.2071 4.08571L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L8.56066 4.56059C8.3654 4.75585 8.04882 4.75585 7.85355 4.56059L8.56066 3.85348L9.26777 3.14637C8.68198 2.56058 7.7322Line truncated
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_자물쇠">
|
||||
<rect width="20" height="20" rx="2" fill="black" fill-opacity="0.25"/>
|
||||
<g id="Union">
|
||||
<mask id="path-2-outside-1_0_34" maskUnits="userSpaceOnUse" x="3" y="2" width="14" height="16" fill="black">
|
||||
<rect fill="white" x="3" y="2" width="14" height="16"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z" fill="#777777"/>
|
||||
<path d="M13 8V9H14V8H13ZM7 8H6V9H7V8ZM6 8V9H7V8H6ZM14 8H13V9H14V8ZM12.2041 3.01074L12.305 2.01584L12.2809 2.0134L12.2567 2.01212L12.2041 3.01074ZM12 3L12.0526 2.00138L12.0263 2H12V3ZM13 8H14V5H13H12V8H13ZM13 5H14C14 3.89543 13.1046 3 12 3V4V5H13ZM12 4V3H8V4V5H12V4ZM8 4V3C6.89543 3 6 3.89543 6 5H7H8V4ZM7 5H6V8H7H8V5H7ZM7 8V9H13V8V7H7V8ZM5 8V9H6V8V7H5V8ZM4 9H5V8V7C3.89543 7 3 7.89543 3 9H4ZM4 16H5V9H4H3V16H4ZM5 17V16H5H4H3C3 17.1046 3.89543 18 5 18V17ZM15 17V16H5V17V18H15V17ZM16 16H15V17V18C16.1046 18 17 17.1046 17 16H16ZM16 9H15V16H16H17V9H16ZM15 8V9H16H17C17 7.89543 16.1046 7 15 7V8ZM14 8V9H15V8V7H14V8ZM14 5H13V8H14H15V5H14ZM12.2041 3.01074L12.1032 4.00564C12.6068 4.0567 13 4.48311 13 5H14H15C15 3.44559 13.8187 2.16931 12.305 2.01584L12.2041 3.01074ZM12 3L11.9474 3.99862L12.1515 4.00936L12.2041 3.01074L12.2567 2.01212L12.0526 2.00138L12 3ZM8 3V4H12V3V2H8V3ZM6 5H7C7 4.44772 7.44772 4 8 4V3V2C6.34315 2 5 3.34315 5 5H6ZM6 8H7V5H6H5V8H6Z" fill="black" mask="url(#path-2-outside-1_0_34)"/>
|
||||
</g>
|
||||
<path id="Rectangle 7448" d="M4 9C4 8.44772 4.44772 8 5 8H10V17H5C4.44772 17 4 16.5523 4 16V9Z" fill="#E4BF4D"/>
|
||||
<path id="Rectangle 7449" d="M10 8H15C15.5523 8 16 8.44772 16 9V16C16 16.5523 15.5523 17 15 17H10V8Z" fill="#D8A300"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_자물쇠">
|
||||
<rect width="20" height="20" rx="2" fill="black" fill-opacity="0.25"/>
|
||||
<g id="Union">
|
||||
<mask id="path-2-outside-1_0_10" maskUnits="userSpaceOnUse" x="3" y="2" width="14" height="16" fill="black">
|
||||
<rect fill="white" x="3" y="2" width="14" height="16"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z" fill="#777777"/>
|
||||
<path d="M13 8V9H14V8H13ZM7 8H6V9H7V8ZM6 8V9H7V8H6ZM14 8H13V9H14V8ZM12.2041 3.01074L12.305 2.01584L12.2809 2.0134L12.2567 2.01212L12.2041 3.01074ZM12 3L12.0526 2.00138L12.0263 2H12V3ZM13 8H14V5H13H12V8H13ZM13 5H14C14 3.89543 13.1046 3 12 3V4V5H13ZM12 4V3H8V4V5H12V4ZM8 4V3C6.89543 3 6 3.89543 6 5H7H8V4ZM7 5H6V8H7H8V5H7ZM7 8V9H13V8V7H7V8ZM5 8V9H6V8V7H5V8ZM4 9H5V8V7C3.89543 7 3 7.89543 3 9H4ZM4 16H5V9H4H3V16H4ZM5 17V16H5H4H3C3 17.1046 3.89543 18 5 18V17ZM15 17V16H5V17V18H15V17ZM16 16H15V17V18C16.1046 18 17 17.1046 17 16H16ZM16 9H15V16H16H17V9H16ZM15 8V9H16H17C17 7.89543 16.1046 7 15 7V8ZM14 8V9H15V8V7H14V8ZM14 5H13V8H14H15V5H14ZM12.2041 3.01074L12.1032 4.00564C12.6068 4.0567 13 4.48311 13 5H14H15C15 3.44559 13.8187 2.16931 12.305 2.01584L12.2041 3.01074ZM12 3L11.9474 3.99862L12.1515 4.00936L12.2041 3.01074L12.2567 2.01212L12.0526 2.00138L12 3ZM8 3V4H12V3V2H8V3ZM6 5H7C7 4.44772 7.44772 4 8 4V3V2C6.34315 2 5 3.34315 5 5H6ZM6 8H7V5H6H5V8H6Z" fill="black" mask="url(#path-2-outside-1_0_10)"/>
|
||||
</g>
|
||||
<path id="Rectangle 7448" d="M4 9C4 8.44772 4.44772 8 5 8H10V17H5C4.44772 17 4 16.5523 4 16V9Z" fill="#BEBEBE"/>
|
||||
<path id="Rectangle 7449" d="M10 8H15C15.5523 8 16 8.44772 16 9V16C16 16.5523 15.5523 17 15 17H10V8Z" fill="#A1A1A1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_자물쇠">
|
||||
<g id="Union">
|
||||
<mask id="path-2-outside-1_0_22" maskUnits="userSpaceOnUse" x="3" y="2" width="14" height="16" fill="black">
|
||||
<rect fill="white" x="3" y="2" width="14" height="16"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z" fill="#777777"/>
|
||||
<path d="M13 8V9H14V8H13ZM7 8H6V9H7V8ZM6 8V9H7V8H6ZM14 8H13V9H14V8ZM12.2041 3.01074L12.305 2.01584L12.2809 2.0134L12.2567 2.01212L12.2041 3.01074ZM12 3L12.0526 2.00138L12.0263 2H12V3ZM13 8H14V5H13H12V8H13ZM13 5H14C14 3.89543 13.1046 3 12 3V4V5H13ZM12 4V3H8V4V5H12V4ZM8 4V3C6.89543 3 6 3.89543 6 5H7H8V4ZM7 5H6V8H7H8V5H7ZM7 8V9H13V8V7H7V8ZM5 8V9H6V8V7H5V8ZM4 9H5V8V7C3.89543 7 3 7.89543 3 9H4ZM4 16H5V9H4H3V16H4ZM5 17V16H5H4H3C3 17.1046 3.89543 18 5 18V17ZM15 17V16H5V17V18H15V17ZM16 16H15V17V18C16.1046 18 17 17.1046 17 16H16ZM16 9H15V16H16H17V9H16ZM15 8V9H16H17C17 7.89543 16.1046 7 15 7V8ZM14 8V9H15V8V7H14V8ZM14 5H13V8H14H15V5H14ZM12.2041 3.01074L12.1032 4.00564C12.6068 4.0567 13 4.48311 13 5H14H15C15 3.44559 13.8187 2.16931 12.305 2.01584L12.2041 3.01074ZM12 3L11.9474 3.99862L12.1515 4.00936L12.2041 3.01074L12.2567 2.01212L12.0526 2.00138L12 3ZM8 3V4H12V3V2H8V3ZM6 5H7C7 4.44772 7.44772 4 8 4V3V2C6.34315 2 5 3.34315 5 5H6ZM6 8H7V5H6H5V8H6Z" fill="black" mask="url(#path-2-outside-1_0_22)"/>
|
||||
</g>
|
||||
<path id="Rectangle 7448" d="M4 9C4 8.44772 4.44772 8 5 8H10V17H5C4.44772 17 4 16.5523 4 16V9Z" fill="#BEBEBE"/>
|
||||
<path id="Rectangle 7449" d="M10 8H15C15.5523 8 16 8.44772 16 9V16C16 16.5523 15.5523 17 15 17H10V8Z" fill="#A1A1A1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_자물쇠">
|
||||
<g id="Union">
|
||||
<mask id="path-2-outside-1_0_3068" maskUnits="userSpaceOnUse" x="3" y="2" width="14" height="16" fill="black">
|
||||
<rect fill="white" x="3" y="2" width="14" height="16"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 8V5C13 4.44772 12.5523 4 12 4H8C7.44772 4 7 4.44772 7 5V8H13ZM6 8H5C4.44772 8 4 8.44772 4 9V16C4 16.5523 4.44772 17 5 17H15C15.5523 17 16 16.5523 16 16V9C16 8.44772 15.5523 8 15 8H14V5C14 3.96435 13.2128 3.113 12.2041 3.01074L12 3H8C6.89543 3 6 3.89543 6 5V8Z" fill="#777777"/>
|
||||
<path d="M13 8V9H14V8H13ZM7 8H6V9H7V8ZM6 8V9H7V8H6ZM14 8H13V9H14V8ZM12.2041 3.01074L12.305 2.01584L12.2809 2.0134L12.2567 2.01212L12.2041 3.01074ZM12 3L12.0526 2.00138L12.0263 2H12V3ZM13 8H14V5H13H12V8H13ZM13 5H14C14 3.89543 13.1046 3 12 3V4V5H13ZM12 4V3H8V4V5H12V4ZM8 4V3C6.89543 3 6 3.89543 6 5H7H8V4ZM7 5H6V8H7H8V5H7ZM7 8V9H13V8V7H7V8ZM5 8V9H6V8V7H5V8ZM4 9H5V8V7C3.89543 7 3 7.89543 3 9H4ZM4 16H5V9H4H3V16H4ZM5 17V16H5H4H3C3 17.1046 3.89543 18 5 18V17ZM15 17V16H5V17V18H15V17ZM16 16H15V17V18C16.1046 18 17 17.1046 17 16H16ZM16 9H15V16H16H17V9H16ZM15 8V9H16H17C17 7.89543 16.1046 7 15 7V8ZM14 8V9H15V8V7H14V8ZM14 5H13V8H14H15V5H14ZM12.2041 3.01074L12.1032 4.00564C12.6068 4.0567 13 4.48311 13 5H14H15C15 3.44559 13.8187 2.16931 12.305 2.01584L12.2041 3.01074ZM12 3L11.9474 3.99862L12.1515 4.00936L12.2041 3.01074L12.2567 2.01212L12.0526 2.00138L12 3ZM8 3V4H12V3V2H8V3ZM6 5H7C7 4.44772 7.44772 4 8 4V3V2C6.34315 2 5 3.34315 5 5H6ZM6 8H7V5H6H5V8H6Z" fill="black" mask="url(#path-2-outside-1_0_3068)"/>
|
||||
</g>
|
||||
<path id="Rectangle 7448" d="M4 9C4 8.44772 4.44772 8 5 8H10V17H5C4.44772 17 4 16.5523 4 16V9Z" fill="#E4BF4D"/>
|
||||
<path id="Rectangle 7449" d="M10 8H15C15.5523 8 16 8.44772 16 9V16C16 16.5523 15.5523 17 15 17H10V8Z" fill="#D8A300"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_눈꽃">
|
||||
<g id="icon_눈꽃_2">
|
||||
<mask id="path-2-outside-1_0_3098" maskUnits="userSpaceOnUse" x="1.5" y="1.5" width="17" height="17" fill="black">
|
||||
<rect fill="white" x="1.5" y="1.5" width="17" height="17"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.5C9.72386 2.5 9.5 2.72386 9.5 3V4.79282L8.56066 3.85348C8.3654 3.65822 8.04882 3.65822 7.85355 3.85348C7.65829 4.04874 7.65829 4.36532 7.85355 4.56058L9.26777 5.9748C9.33429 6.04133 9.4149 6.08519 9.5 6.10638V8.78906L6.40824 5.6973C6.21298 5.50204 5.8964 5.50204 5.70113 5.6973C5.50587 5.89257 5.50587 6.20915 5.70113 6.40441L8.79672 9.5H6.10247C6.08128 9.4149 6.03742 9.33429 5.97089 9.26777L4.55668 7.85355C4.36142 7.65829 4.04483 7.65829 3.84957 7.85355C3.65431 8.04882 3.65431 8.3654 3.84957 8.56066L4.78891 9.5H3C2.72386 9.5 2.5 9.72386 2.5 10C2.5 10.2761 2.72386 10.5 3 10.5H4.61704L3.84957 11.2675C3.65431 11.4627 3.65431 11.7793 3.84957 11.9746C4.04483 12.1698 4.36142 12.1698 4.55668 11.9746L5.97089 10.5604C5.98993 10.5413 6.00712 10.5211 6.02244 10.5H8.79688L5.11429 14.1826C4.91903 14.3778 4.91903 14.6944 5.11429 14.8897C5.30955 15.085 5.62614 15.085 5.8214 14.8897L9.5 11.2111V13.7217C9.4149 13.7429 9.33429 13.7868 9.26777 13.8533L7.85355 15.2675C7.65829 15.4628 7.65829 15.7794 7.85355 15.9746C8.04882 16.1699 8.3654 16.1699 8.56066 15.9746L9.5 15.0353V17C9.5 17.2761 9.72386 17.5 10 17.5C10.2761 17.5 10.5 17.2761 10.5 17V15.2072L11.2675 15.9746C11.4627 16.1699 11.7793 16.1699 11.9746 15.9746C12.1698 15.7794 12.1698 15.4628 11.9746 15.2675L10.5604 13.8533C10.5413 13.8343 10.5211 13.8171 10.5 13.8018V11.2033L14.1864 14.8897C14.3817 15.085 14.6983 15.085 14.8935 14.8897C15.0888 14.6944 15.0888 14.3778 14.8935 14.1826L11.2109 10.5H13.8057C13.821 10.5211 13.8382 10.5413 13.8572 10.5604L15.2714 11.9746C15.4667 12.1698 15.7833 12.1698 15.9786 11.9746C16.1738 11.7793 16.1738 11.4627 15.9786 11.2675L15.2111 10.5H17C17.2761 10.5 17.5 10.2761 17.5 10C17.5 9.72386 17.2761 9.5 17 9.5H15.0392L15.9786 8.56066C16.1738 8.3654 16.1738 8.04882 15.9786 7.85355C15.7833 7.65829 15.4667 7.65829 15.2714 7.85355L13.8572 9.26777C13.7907 9.33429 13.7468 9.4149 13.7257 9.5H11.2111L14.3067 6.40441C14.5019 6.20915 14.5019 5.89257 14.3067 5.6973C14.1114 5.50204 13.7948 5.50204 13.5996 5.6973L10.5 8.79688V6.02635C10.5211 6.01102 10.5413 5.99384 10.5604 5.9748L11.9746 4.56058C12.1698 4.36532 12.1698 4.04874 11.9746 3.85348C11.7793 3.65822 11.4627 3.65822 11.2675 3.85348L10.5 4.62094V3C10.5 2.72386 10.2761 2.5 10 2.5Z" fill="#00CCFF"/>
|
||||
<path d="M9.5 4.79282L8.79289 5.49992C9.07889 5.78592 9.50901 5.87148 9.88268 5.7167C10.2564 5.56192 10.5 5.19728 10.5 4.79282H9.5ZM8.56066 3.85348L9.26777 3.14637L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L7.14645 3.14637L7.14645 3.14637L7.85355 3.85348ZM7.85355 4.56058L7.14645 5.26769L7.14645 5.26769L7.85355 4.56058ZM9.26777 5.9748L9.97487 5.26769V5.26769L9.26777 5.9748ZM9.5 6.10638H10.5C10.5 5.64719 10.1873 5.24701 9.74168 5.13603L9.5 6.10638ZM9.5 8.78906L8.79289 9.49617C9.07889 9.78217 9.50901 9.86772 9.88268 9.71294C10.2564 9.55816 10.5 9.19352 10.5 8.78906H9.5ZM5.70113 6.40441L6.40824 5.6973V5.6973L5.70113 6.40441ZM8.79672 9.5V10.5C9.20119 10.5 9.56582 10.2564 9.7206 9.88268C9.87538 9.50901 9.78983 9.07889 9.50383 8.79289L8.79672 9.5ZM6.10247 9.5L5.13212 9.74168C5.2431 10.1873 5.64328 10.5 6.10247 10.5V9.5ZM5.97089 9.26777L5.26378 9.97487V9.97487L5.97089 9.26777ZM4.55668 7.85355L5.26378 7.14645L5.26378 7.14645L4.55668 7.85355ZM3.84957 7.85355L3.14246 7.14645L3.14246 7.14645L3.84957 7.85355ZM3.84957 8.56066L3.14246 9.26777L3.14246 9.26777L3.84957 8.56066ZM4.78891 9.5V10.5C5.19337 10.5 5.55801 10.2564 5.71279 9.88268C5.86757 9.50901 5.78202 9.07889 5.49602 8.79289L4.78891 9.5ZM4.61704 10.5L5.32414 11.2071C5.61014 10.9211 5.6957 10.491 5.54092 10.1173C5.38614 9.74364 5.0215 9.5 4.61704 9.5V10.5ZM3.84957 11.2675L4.55668 11.9746L4.55668 11.9746L3.84957 11.2675ZM3.84957 11.9746L4.55668 11.2675L4.55668 11.2675L3.84957 11.9746ZM4.55668 11.9746L3.84957 11.2675L3.84957 11.2675L4.55668 11.9746ZM5.97089 10.5604L6.678 11.2675L6.678 11.2675L5.97089 10.5604ZM6.02244 10.5V9.5C5.70212 9.5 5.40119 9.65345 5.21307 9.91271L6.02244 10.5ZM8.79688 10.5L9.50398 11.2071C9.78998 10.9211 9.87554 10.491 9.72076 10.1173C9.56597 9.74364 9.20134 9.5 8.79688 9.5V10.5ZM5.11429 14.1826L5.8214 14.8897L5.8214 14.8897L5.11429 14.1826ZM5.11429 14.8897L5.8214 14.1826L5.8214 14.1826L5.11429 14.8897ZM9.5 11.2111H10.5C10.5 10.8066 10.2564 10.442 9.88268 10.2872C9.50901 10.1324 9.07889 10.218 8.79289 10.504L9.5 11.2111ZM9.5 13.7217L9.74168 14.6921C10.1873 14.5811 10.5 14.1809 10.5 13.7217H9.5ZM9.26777 13.8533L8.56066 13.1462L8.56066 13.1462L9.26777 13.8533ZM7.85355 15.2675L7.14645 14.5604L7.14644 14.5604L7.85355 15.2675ZM7.85355 15.9746L7.14644 16.6818L7.14645 16.6818L7.85355 15.9746ZM8.56066 15.9746L7.85355 15.2675L7.85355 15.2675L8.56066 15.9746ZM9.5 15.0353H10.5C10.5 14.6308 10.2564 14.2662 9.88268 14.1114C9.50901 13.9566 9.07889 14.0422 8.79289 14.3282L9.5 15.0353ZM10.5 15.2072L11.2071 14.5001C10.9211 14.2141 10.491 14.1285 10.1173 14.2833C9.74364 14.4381 9.5 14.8027 9.5 15.2072H10.5ZM11.2675 15.9746L11.9746 15.2675L11.9746 15.2675L11.2675 15.9746ZM10.5 13.8018H9.5C9.5 14.1221 9.65345 14.423 9.91271 14.6111L10.5 13.8018ZM10.5 11.2033L11.2071 10.4962C10.9211 10.2102 10.491 10.1246 10.1173 10.2794C9.74364 10.4342 9.5 10.7988 9.5 11.2033H10.5ZM14.1864 14.8897L13.4793 15.5968L13.4793 15.5968L14.1864 14.8897ZM11.2109 10.5V9.5C10.8065 9.5 10.4418 9.74364 10.2871 10.1173C10.1323 10.491 10.2178 10.9211 10.5038 11.2071L11.2109 10.5ZM13.8057 10.5L14.615 9.9127C14.4269 9.65344 14.126 9.5 13.8057 9.5V10.5ZM15.9786 11.2675L16.6857 10.5604V10.5604L15.9786 11.2675ZM15.2111 10.5V9.5C14.8066 9.5 14.442 9.74364 14.2872 10.1173C14.1324 10.491 14.218 10.9211 14.504 11.2071L15.2111 10.5ZM15.0392 9.5L14.3321 8.79289C14.0461 9.07889 13.9606 9.50901 14.1153 9.88268C14.2701 10.2564 14.6348 10.5 15.0392 10.5V9.5ZM15.9786 8.56066L15.2714 7.85355L15.2714 7.85355L15.9786 8.56066ZM15.9786 7.85355L16.6857 7.14645L16.6857 7.14644L15.9786 7.85355ZM15.2714 7.85355L14.5643 7.14644L14.5643 7.14645L15.2714 7.85355ZM13.8572 9.26777L14.5643 9.97488L14.5643 9.97487L13.8572 9.26777ZM13.7257 9.5V10.5C14.1848 10.5 14.585 10.1873 14.696 9.74168L13.7257 9.5ZM11.2111 9.5L10.504 8.79289C10.218 9.07889 10.1324 9.50901 10.2872 9.88268C10.442 10.2564 10.8066 10.5 11.2111 10.5V9.5ZM14.3067 6.40441L15.0138 7.11152L15.0138 7.11152L14.3067 6.40441ZM14.3067 5.6973L15.0138 4.9902L15.0138 4.99019L14.3067 5.6973ZM13.5996 5.6973L12.8925 4.9902V4.9902L13.5996 5.6973ZM10.5 8.79688H9.5C9.5 9.20134 9.74364 9.56597 10.1173 9.72076C10.491 9.87554 10.9211 9.78998 11.2071 9.50398L10.5 8.79688ZM10.5 6.02635L9.91271 5.21698C9.65345 5.4051 9.5 5.70603 9.5 6.02635H10.5ZM10.5604 5.9748L11.2675 6.68191L11.2675 6.6819L10.5604 5.9748ZM11.9746 4.56058L11.2675 3.85348L11.2675 3.85348L11.9746 4.56058ZM11.9746 3.85348L11.2675 4.56058L11.2675 4.56059L11.9746 3.85348ZM11.2675 3.85348L11.9746 4.56058L11.9746 4.56058L11.2675 3.85348ZM10.5 4.62094H9.5C9.5 5.02541 9.74364 5.39004 10.1173 5.54482C10.491 5.6996 10.9211 5.61405 11.2071 5.32805L10.5 4.62094ZM9.5 3H10.5C10.5 3.27614 10.2761 3.5 10 3.5V2.5V1.5C9.17157 1.5 8.5 2.17157 8.5 3H9.5ZM9.5 4.79282H10.5V3H9.5H8.5V4.79282H9.5ZM8.56066 3.85348L7.85355 4.56058L8.79289 5.49992L9.5 4.79282L10.2071 4.08571L9.26777 3.14637L8.56066 3.85348ZM7.85355 3.85348L8.56066 4.56059C8.3654 4.75585 8.04882 4.75585 7.85355 4.56059L8.56066 3.85348L9.26777 3.14637C8.68198 2.56058 7.7322Line truncated
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="14" height="18" viewBox="0 0 14 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_2922" maskUnits="userSpaceOnUse" x="0" y="0" width="14" height="18" fill="black">
|
||||
<rect fill="white" width="14" height="18"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 12.1973C11.7934 11.1599 13 9.22085 13 7C13 3.68629 10.3137 1 7 1C3.68629 1 1 3.68629 1 7C1 9.22085 2.2066 11.1599 4 12.1973V16C4 16.5523 4.44772 17 5 17H9C9.55228 17 10 16.5523 10 16V12.1973Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 12.1973C11.7934 11.1599 13 9.22085 13 7C13 3.68629 10.3137 1 7 1C3.68629 1 1 3.68629 1 7C1 9.22085 2.2066 11.1599 4 12.1973V16C4 16.5523 4.44772 17 5 17H9C9.55228 17 10 16.5523 10 16V12.1973Z" fill="#D9D9D9"/>
|
||||
<path d="M10 12.1973L9.49927 11.3317L9 11.6205V12.1973H10ZM4 12.1973H5V11.6205L4.50073 11.3317L4 12.1973ZM13 7H12C12 8.84945 10.9964 10.4657 9.49927 11.3317L10 12.1973L10.5007 13.0629C12.5904 11.8541 14 9.59225 14 7H13ZM7 1V2C9.76142 2 12 4.23858 12 7H13H14C14 3.13401 10.866 0 7 0V1ZM1 7H2C2 4.23858 4.23858 2 7 2V1V0C3.13401 0 0 3.13401 0 7H1ZM4 12.1973L4.50073 11.3317C3.00361 10.4657 2 8.84945 2 7H1H0C0 9.59225 1.40958 11.8541 3.49927 13.0629L4 12.1973ZM4 16H5V12.1973H4H3V16H4ZM5 17V16H4H3C3 17.1046 3.89543 18 5 18V17ZM9 17V16H5V17V18H9V17ZM10 16H9V17V18C10.1046 18 11 17.1046 11 16H10ZM10 12.1973H9V16H10H11V12.1973H10Z" fill="black" mask="url(#path-1-outside-1_0_2922)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="60" height="15" viewBox="0 0 60 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319903">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_2950" maskUnits="userSpaceOnUse" x="0" y="0" width="60" height="15" fill="black">
|
||||
<rect fill="white" width="60" height="15"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z" fill="#FFC600"/>
|
||||
<path d="M2 1H3V0H2V1ZM1 1V0H0V1H1ZM2 14V15H3V14H2ZM1 14H0V15H1V14ZM53.5 7V8H54.5V7H53.5ZM7.5 7H6.5V8H7.5V7ZM7.5 4.46891H8.5V2.58964L6.94134 3.63952L7.5 4.46891ZM3 7.5L2.44134 6.6706L1.21 7.5L2.44134 8.3294L3 7.5ZM7.5 10.5311L6.94134 11.3605L8.5 12.4104V10.5311H7.5ZM7.5 8V7H6.5V8H7.5ZM53.5 8H54.5V7H53.5V8ZM53.5 10.5311H52.5V12.4104L54.0587 11.3605L53.5 10.5311ZM58 14H57V15H58V14ZM59 14V15H60V14H59ZM59 1H60V0H59V1ZM58 1V0H57V1H58ZM53.5 4.46891L54.0587 3.63952L52.5 2.58964V4.46891H53.5ZM2 1V0H1V1V2H2V1ZM2 14H3V1H2H1V14H2ZM1 14V15H2V14V13H1V14ZM1 1H0V14H1H2V1H1ZM53.5 7V6L7.5 6V7V8L53.5 8V7ZM7.5 7H8.5V4.46891H7.5H6.5V7H7.5ZM7.5 4.46891L6.94134 3.63952L2.44134 6.6706L3 7.5L3.55866 8.3294L8.05866 5.29831L7.5 4.46891ZM3 7.5L2.44134 8.3294L6.94134 11.3605L7.5 10.5311L8.05866 9.70169L3.55866 6.6706L3 7.5ZM7.5 10.5311H8.5V8H7.5H6.5V10.5311H7.5ZM7.5 8V9L53.5 9V8V7L7.5 7V8ZM53.5 8H52.5V10.5311H53.5H54.5V8H53.5ZM53.5 10.5311L54.0587 11.3605L58.5587 8.3294L58 7.5L57.4413 6.6706L52.9413 9.70169L53.5 10.5311ZM58 14H59V7.5H58H57V14H58ZM59 14V13H58V14V15H59V14ZM59 1H58V14H59H60V1H59ZM58 1V2H59V1V0H58V1ZM58 7.5H59V1H58H57V7.5H58ZM58 7.5L58.5587 6.6706L54.0587 3.63952L53.5 4.46891L52.9413 5.29831L57.4413 8.3294L58 7.5ZM53.5 4.46891H52.5V7H53.5H54.5V4.46891H53.5Z" fill="black" mask="url(#path-1-outside-1_0_2950)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="60" height="15" viewBox="0 0 60 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319903">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_2950" maskUnits="userSpaceOnUse" x="0" y="0" width="60" height="15" fill="black">
|
||||
<rect fill="white" width="60" height="15"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z" fill="white"/>
|
||||
<path d="M2 1H3V0H2V1ZM1 1V0H0V1H1ZM2 14V15H3V14H2ZM1 14H0V15H1V14ZM53.5 7V8H54.5V7H53.5ZM7.5 7H6.5V8H7.5V7ZM7.5 4.46891H8.5V2.58964L6.94134 3.63952L7.5 4.46891ZM3 7.5L2.44134 6.6706L1.21 7.5L2.44134 8.3294L3 7.5ZM7.5 10.5311L6.94134 11.3605L8.5 12.4104V10.5311H7.5ZM7.5 8V7H6.5V8H7.5ZM53.5 8H54.5V7H53.5V8ZM53.5 10.5311H52.5V12.4104L54.0587 11.3605L53.5 10.5311ZM58 14H57V15H58V14ZM59 14V15H60V14H59ZM59 1H60V0H59V1ZM58 1V0H57V1H58ZM53.5 4.46891L54.0587 3.63952L52.5 2.58964V4.46891H53.5ZM2 1V0H1V1V2H2V1ZM2 14H3V1H2H1V14H2ZM1 14V15H2V14V13H1V14ZM1 1H0V14H1H2V1H1ZM53.5 7V6L7.5 6V7V8L53.5 8V7ZM7.5 7H8.5V4.46891H7.5H6.5V7H7.5ZM7.5 4.46891L6.94134 3.63952L2.44134 6.6706L3 7.5L3.55866 8.3294L8.05866 5.29831L7.5 4.46891ZM3 7.5L2.44134 8.3294L6.94134 11.3605L7.5 10.5311L8.05866 9.70169L3.55866 6.6706L3 7.5ZM7.5 10.5311H8.5V8H7.5H6.5V10.5311H7.5ZM7.5 8V9L53.5 9V8V7L7.5 7V8ZM53.5 8H52.5V10.5311H53.5H54.5V8H53.5ZM53.5 10.5311L54.0587 11.3605L58.5587 8.3294L58 7.5L57.4413 6.6706L52.9413 9.70169L53.5 10.5311ZM58 14H59V7.5H58H57V14H58ZM59 14V13H58V14V15H59V14ZM59 1H58V14H59H60V1H59ZM58 1V2H59V1V0H58V1ZM58 7.5H59V1H58H57V7.5H58ZM58 7.5L58.5587 6.6706L54.0587 3.63952L53.5 4.46891L52.9413 5.29831L57.4413 8.3294L58 7.5ZM53.5 4.46891H52.5V7H53.5H54.5V4.46891H53.5Z" fill="black" mask="url(#path-1-outside-1_0_2950)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="60" height="15" viewBox="0 0 60 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319903">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_8" maskUnits="userSpaceOnUse" x="0" y="0" width="60" height="15" fill="black">
|
||||
<rect fill="white" width="60" height="15"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z" fill="#FF5C00"/>
|
||||
<path d="M2 1H3V0H2V1ZM1 1V0H0V1H1ZM2 14V15H3V14H2ZM1 14H0V15H1V14ZM53.5 7V8H54.5V7H53.5ZM7.5 7H6.5V8H7.5V7ZM7.5 4.46891H8.5V2.58964L6.94134 3.63952L7.5 4.46891ZM3 7.5L2.44134 6.6706L1.21 7.5L2.44134 8.3294L3 7.5ZM7.5 10.5311L6.94134 11.3605L8.5 12.4104V10.5311H7.5ZM7.5 8V7H6.5V8H7.5ZM53.5 8H54.5V7H53.5V8ZM53.5 10.5311H52.5V12.4104L54.0587 11.3605L53.5 10.5311ZM58 14H57V15H58V14ZM59 14V15H60V14H59ZM59 1H60V0H59V1ZM58 1V0H57V1H58ZM53.5 4.46891L54.0587 3.63952L52.5 2.58964V4.46891H53.5ZM2 1V0H1V1V2H2V1ZM2 14H3V1H2H1V14H2ZM1 14V15H2V14V13H1V14ZM1 1H0V14H1H2V1H1ZM53.5 7V6L7.5 6V7V8L53.5 8V7ZM7.5 7H8.5V4.46891H7.5H6.5V7H7.5ZM7.5 4.46891L6.94134 3.63952L2.44134 6.6706L3 7.5L3.55866 8.3294L8.05866 5.29831L7.5 4.46891ZM3 7.5L2.44134 8.3294L6.94134 11.3605L7.5 10.5311L8.05866 9.70169L3.55866 6.6706L3 7.5ZM7.5 10.5311H8.5V8H7.5H6.5V10.5311H7.5ZM7.5 8V9L53.5 9V8V7L7.5 7V8ZM53.5 8H52.5V10.5311H53.5H54.5V8H53.5ZM53.5 10.5311L54.0587 11.3605L58.5587 8.3294L58 7.5L57.4413 6.6706L52.9413 9.70169L53.5 10.5311ZM58 14H59V7.5H58H57V14H58ZM59 14V13H58V14V15H59V14ZM59 1H58V14H59H60V1H59ZM58 1V2H59V1V0H58V1ZM58 7.5H59V1H58H57V7.5H58ZM58 7.5L58.5587 6.6706L54.0587 3.63952L53.5 4.46891L52.9413 5.29831L57.4413 8.3294L58 7.5ZM53.5 4.46891H52.5V7H53.5H54.5V4.46891H53.5Z" fill="black" mask="url(#path-1-outside-1_0_8)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="60" height="15" viewBox="0 0 60 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319903">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_15" maskUnits="userSpaceOnUse" x="0" y="0" width="60" height="15" fill="black">
|
||||
<rect fill="white" width="60" height="15"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1H2V14H1V1ZM53.5 7L7.5 7V4.46891L3 7.5L7.5 10.5311V8L53.5 8V10.5311L58 7.5V14H59V1H58V7.5L53.5 4.46891V7Z" fill="#F6F4F2"/>
|
||||
<path d="M2 1H3V0H2V1ZM1 1V0H0V1H1ZM2 14V15H3V14H2ZM1 14H0V15H1V14ZM53.5 7V8H54.5V7H53.5ZM7.5 7H6.5V8H7.5V7ZM7.5 4.46891H8.5V2.58964L6.94134 3.63952L7.5 4.46891ZM3 7.5L2.44134 6.6706L1.21 7.5L2.44134 8.3294L3 7.5ZM7.5 10.5311L6.94134 11.3605L8.5 12.4104V10.5311H7.5ZM7.5 8V7H6.5V8H7.5ZM53.5 8H54.5V7H53.5V8ZM53.5 10.5311H52.5V12.4104L54.0587 11.3605L53.5 10.5311ZM58 14H57V15H58V14ZM59 14V15H60V14H59ZM59 1H60V0H59V1ZM58 1V0H57V1H58ZM53.5 4.46891L54.0587 3.63952L52.5 2.58964V4.46891H53.5ZM2 1V0H1V1V2H2V1ZM2 14H3V1H2H1V14H2ZM1 14V15H2V14V13H1V14ZM1 1H0V14H1H2V1H1ZM53.5 7V6L7.5 6V7V8L53.5 8V7ZM7.5 7H8.5V4.46891H7.5H6.5V7H7.5ZM7.5 4.46891L6.94134 3.63952L2.44134 6.6706L3 7.5L3.55866 8.3294L8.05866 5.29831L7.5 4.46891ZM3 7.5L2.44134 8.3294L6.94134 11.3605L7.5 10.5311L8.05866 9.70169L3.55866 6.6706L3 7.5ZM7.5 10.5311H8.5V8H7.5H6.5V10.5311H7.5ZM7.5 8V9L53.5 9V8V7L7.5 7V8ZM53.5 8H52.5V10.5311H53.5H54.5V8H53.5ZM53.5 10.5311L54.0587 11.3605L58.5587 8.3294L58 7.5L57.4413 6.6706L52.9413 9.70169L53.5 10.5311ZM58 14H59V7.5H58H57V14H58ZM59 14V13H58V14V15H59V14ZM59 1H58V14H59H60V1H59ZM58 1V2H59V1V0H58V1ZM58 7.5H59V1H58H57V7.5H58ZM58 7.5L58.5587 6.6706L54.0587 3.63952L53.5 4.46891L52.9413 5.29831L57.4413 8.3294L58 7.5ZM53.5 4.46891H52.5V7H53.5H54.5V4.46891H53.5Z" fill="black" mask="url(#path-1-outside-1_0_15)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,40 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="40" height="38" viewBox="0 0 40 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="꾸러미ver2/팝업가이드 ">
|
||||
<rect id="Rectangle 6961" x="0.5" y="0.5" width="39" height="37" rx="1.5" fill="url(#paint0_linear_0_3057)" stroke="url(#paint1_linear_0_3057)"/>
|
||||
<g id="Union" filter="url(#filter0_d_0_3057)">
|
||||
<mask id="path-2-outside-1_0_3057" maskUnits="userSpaceOnUse" x="10" y="9.5" width="20" height="20" fill="black">
|
||||
<rect fill="white" x="10" y="9.5" width="20" height="20"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 12H27C27.2761 12 27.5 12.2239 27.5 12.5V26.5C27.5 26.7761 27.2761 27 27 27H13C12.7239 27 12.5 26.7761 12.5 26.5V12.5C12.5 12.2239 12.7239 12 13 12ZM11 12.5C11 11.3954 11.8954 10.5 13 10.5H27C28.1046 10.5 29 11.3954 29 12.5V26.5C29 27.6046 28.1046 28.5 27 28.5H13C11.8954 28.5 11 27.6046 11 26.5V12.5ZM17.3152 14.424V24H19.0312V22.32L20.0032 21.216L21.6352 24H23.5432L20.9992 20.028L23.3272 17.28H21.4072L19.0792 20.184H19.0312V14.424H17.3152Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 12H27C27.2761 12 27.5 12.2239 27.5 12.5V26.5C27.5 26.7761 27.2761 27 27 27H13C12.7239 27 12.5 26.7761 12.5 26.5V12.5C12.5 12.2239 12.7239 12 13 12ZM11 12.5C11 11.3954 11.8954 10.5 13 10.5H27C28.1046 10.5 29 11.3954 29 12.5V26.5C29 27.6046 28.1046 28.5 27 28.5H13C11.8954 28.5 11 27.6046 11 26.5V12.5ZM17.3152 14.424V24H19.0312V22.32L20.0032 21.216L21.6352 24H23.5432L20.9992 20.028L23.3272 17.28H21.4072L19.0792 20.184H19.0312V14.424H17.3152Z" fill="#FFC600"/>
|
||||
<path d="M17.3152 24H16.3152V25H17.3152V24ZM17.3152 14.424V13.424H16.3152V14.424H17.3152ZM19.0312 24V25H20.0312V24H19.0312ZM19.0312 22.32L18.2806 21.6592L18.0312 21.9425V22.32H19.0312ZM20.0032 21.216L20.8659 20.7103L20.1665 19.5172L19.2526 20.5552L20.0032 21.216ZM21.6352 24L20.7725 24.5057L21.0622 25H21.6352V24ZM23.5432 24V25H25.3712L24.3853 23.4607L23.5432 24ZM20.9992 20.028L20.2362 19.3816L19.7587 19.9453L20.1571 20.5673L20.9992 20.028ZM23.3272 17.28L24.0902 17.9264L25.485 16.28H23.3272V17.28ZM21.4072 17.28V16.28H20.9272L20.6269 16.6545L21.4072 17.28ZM19.0792 20.184V21.184H19.5592L19.8594 20.8095L19.0792 20.184ZM19.0312 20.184H18.0312V21.184H19.0312V20.184ZM19.0312 14.424H20.0312V13.424H19.0312V14.424ZM27 12V11H13V12V13H27V12ZM27.5 12.5H28.5C28.5 11.6716 27.8284 11 27 11V12V13C26.7239 13 26.5 12.7761 26.5 12.5H27.5ZM27.5 26.5H28.5V12.5H27.5H26.5V26.5H27.5ZM27 27V28C27.8284 28 28.5 27.3284 28.5 26.5H27.5H26.5C26.5 26.2239 26.7239 26 27 26V27ZM13 27V28H27V27V26H13V27ZM12.5 26.5H11.5C11.5 27.3284 12.1716 28 13 28V27V26C13.2761 26 13.5 26.2239 13.5 26.5H12.5ZM12.5 12.5H11.5V26.5H12.5H13.5V12.5H12.5ZM13 12V11C12.1716 11 11.5 11.6716 11.5 12.5H12.5H13.5C13.5 12.7761 13.2761 13 13 13V12ZM13 10.5V9.5C11.3431 9.5 10 10.8431 10 12.5H11H12C12 11.9477 12.4477 11.5 13 11.5V10.5ZM27 10.5V9.5H13V10.5V11.5H27V10.5ZM29 12.5H30C30 10.8431 28.6569 9.5 27 9.5V10.5V11.5C27.5523 11.5 28 11.9477 28 12.5H29ZM29 26.5H30V12.5H29H28V26.5H29ZM27 28.5V29.5C28.6569 29.5 30 28.1569 30 26.5H29H28C28 27.0523 27.5523 27.5 27 27.5V28.5ZM13 28.5V29.5H27V28.5V27.5H13V28.5ZM11 26.5H10C10 28.1569 11.3431 29.5 13 29.5V28.5V27.5C12.4477 27.5 12 27.0523 12 26.5H11ZM11 12.5H10V26.5H11H12V12.5H11ZM17.3152 24H18.3152V14.424H17.3152H16.3152V24H17.3152ZM19.0312 24V23H17.3152V24V25H19.0312V24ZM19.0312 22.32H18.0312V24H19.0312H20.0312V22.32H19.0312ZM20.0032 21.216L19.2526 20.5552L18.2806 21.6592L19.0312 22.32L19.7817 22.9808L20.7537 21.8768L20.0032 21.216ZM21.6352 24L22.4979 23.4943L20.8659 20.7103L20.0032 21.216L19.1405 21.7217L20.7725 24.5057L21.6352 24ZM23.5432 24V23H21.6352V24V25H23.5432V24ZM20.9992 20.028L20.1571 20.5673L22.7011 24.5393L23.5432 24L24.3853 23.4607L21.8413 19.4887L20.9992 20.028ZM23.3272 17.28L22.5642 16.6336L20.2362 19.3816L20.9992 20.028L21.7622 20.6744L24.0902 17.9264L23.3272 17.28ZM21.4072 17.28V18.28H23.3272V17.28V16.28H21.4072V17.28ZM19.0792 20.184L19.8594 20.8095L22.1874 17.9055L21.4072 17.28L20.6269 16.6545L18.2989 19.5585L19.0792 20.184ZM19.0312 20.184V21.184H19.0792V20.184V19.184H19.0312V20.184ZM19.0312 14.424H18.0312V20.184H19.0312H20.0312V14.424H19.0312ZM17.3152 14.424V15.424H19.0312V14.424V13.424H17.3152V14.424Z" fill="black" mask="url(#path-2-outside-1_0_3057)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_3057" x="6.77366" y="6.27366" width="26.4527" height="26.4527" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="1.61317"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0235294 0 0 0 0 0.101961 0 0 0 0 0.0784314 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_3057"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_3057" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_3057" x1="20" y1="0" x2="20" y2="38" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#081813"/>
|
||||
<stop offset="0.086884" stop-color="#09271E"/>
|
||||
<stop offset="0.254279" stop-color="#0B3227"/>
|
||||
<stop offset="0.512521" stop-color="#0E3D2F"/>
|
||||
<stop offset="0.800267" stop-color="#0B3227"/>
|
||||
<stop offset="0.897594" stop-color="#0A2B21"/>
|
||||
<stop offset="1" stop-color="#081813"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_0_3057" x1="20" y1="0" x2="20" y2="38" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#08251C"/>
|
||||
<stop offset="0.465449" stop-color="#208769"/>
|
||||
<stop offset="0.844436" stop-color="#08241C"/>
|
||||
<stop offset="1" stop-color="#051612"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,40 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="40" height="38" viewBox="0 0 40 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="꾸러미ver2/팝업가이드 ">
|
||||
<rect id="Rectangle 6961" x="0.5" y="0.5" width="39" height="37" rx="1.5" fill="url(#paint0_linear_0_3057)" stroke="url(#paint1_linear_0_3057)"/>
|
||||
<g id="Union" filter="url(#filter0_d_0_3057)">
|
||||
<mask id="path-2-outside-1_0_3057" maskUnits="userSpaceOnUse" x="10" y="9.5" width="20" height="20" fill="black">
|
||||
<rect fill="white" x="10" y="9.5" width="20" height="20"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 12H27C27.2761 12 27.5 12.2239 27.5 12.5V26.5C27.5 26.7761 27.2761 27 27 27H13C12.7239 27 12.5 26.7761 12.5 26.5V12.5C12.5 12.2239 12.7239 12 13 12ZM11 12.5C11 11.3954 11.8954 10.5 13 10.5H27C28.1046 10.5 29 11.3954 29 12.5V26.5C29 27.6046 28.1046 28.5 27 28.5H13C11.8954 28.5 11 27.6046 11 26.5V12.5ZM17.3152 14.424V24H19.0312V22.32L20.0032 21.216L21.6352 24H23.5432L20.9992 20.028L23.3272 17.28H21.4072L19.0792 20.184H19.0312V14.424H17.3152Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 12H27C27.2761 12 27.5 12.2239 27.5 12.5V26.5C27.5 26.7761 27.2761 27 27 27H13C12.7239 27 12.5 26.7761 12.5 26.5V12.5C12.5 12.2239 12.7239 12 13 12ZM11 12.5C11 11.3954 11.8954 10.5 13 10.5H27C28.1046 10.5 29 11.3954 29 12.5V26.5C29 27.6046 28.1046 28.5 27 28.5H13C11.8954 28.5 11 27.6046 11 26.5V12.5ZM17.3152 14.424V24H19.0312V22.32L20.0032 21.216L21.6352 24H23.5432L20.9992 20.028L23.3272 17.28H21.4072L19.0792 20.184H19.0312V14.424H17.3152Z" fill="#B6D0C9"/>
|
||||
<path d="M17.3152 24H16.3152V25H17.3152V24ZM17.3152 14.424V13.424H16.3152V14.424H17.3152ZM19.0312 24V25H20.0312V24H19.0312ZM19.0312 22.32L18.2806 21.6592L18.0312 21.9425V22.32H19.0312ZM20.0032 21.216L20.8659 20.7103L20.1665 19.5172L19.2526 20.5552L20.0032 21.216ZM21.6352 24L20.7725 24.5057L21.0622 25H21.6352V24ZM23.5432 24V25H25.3712L24.3853 23.4607L23.5432 24ZM20.9992 20.028L20.2362 19.3816L19.7587 19.9453L20.1571 20.5673L20.9992 20.028ZM23.3272 17.28L24.0902 17.9264L25.485 16.28H23.3272V17.28ZM21.4072 17.28V16.28H20.9272L20.6269 16.6545L21.4072 17.28ZM19.0792 20.184V21.184H19.5592L19.8594 20.8095L19.0792 20.184ZM19.0312 20.184H18.0312V21.184H19.0312V20.184ZM19.0312 14.424H20.0312V13.424H19.0312V14.424ZM27 12V11H13V12V13H27V12ZM27.5 12.5H28.5C28.5 11.6716 27.8284 11 27 11V12V13C26.7239 13 26.5 12.7761 26.5 12.5H27.5ZM27.5 26.5H28.5V12.5H27.5H26.5V26.5H27.5ZM27 27V28C27.8284 28 28.5 27.3284 28.5 26.5H27.5H26.5C26.5 26.2239 26.7239 26 27 26V27ZM13 27V28H27V27V26H13V27ZM12.5 26.5H11.5C11.5 27.3284 12.1716 28 13 28V27V26C13.2761 26 13.5 26.2239 13.5 26.5H12.5ZM12.5 12.5H11.5V26.5H12.5H13.5V12.5H12.5ZM13 12V11C12.1716 11 11.5 11.6716 11.5 12.5H12.5H13.5C13.5 12.7761 13.2761 13 13 13V12ZM13 10.5V9.5C11.3431 9.5 10 10.8431 10 12.5H11H12C12 11.9477 12.4477 11.5 13 11.5V10.5ZM27 10.5V9.5H13V10.5V11.5H27V10.5ZM29 12.5H30C30 10.8431 28.6569 9.5 27 9.5V10.5V11.5C27.5523 11.5 28 11.9477 28 12.5H29ZM29 26.5H30V12.5H29H28V26.5H29ZM27 28.5V29.5C28.6569 29.5 30 28.1569 30 26.5H29H28C28 27.0523 27.5523 27.5 27 27.5V28.5ZM13 28.5V29.5H27V28.5V27.5H13V28.5ZM11 26.5H10C10 28.1569 11.3431 29.5 13 29.5V28.5V27.5C12.4477 27.5 12 27.0523 12 26.5H11ZM11 12.5H10V26.5H11H12V12.5H11ZM17.3152 24H18.3152V14.424H17.3152H16.3152V24H17.3152ZM19.0312 24V23H17.3152V24V25H19.0312V24ZM19.0312 22.32H18.0312V24H19.0312H20.0312V22.32H19.0312ZM20.0032 21.216L19.2526 20.5552L18.2806 21.6592L19.0312 22.32L19.7817 22.9808L20.7537 21.8768L20.0032 21.216ZM21.6352 24L22.4979 23.4943L20.8659 20.7103L20.0032 21.216L19.1405 21.7217L20.7725 24.5057L21.6352 24ZM23.5432 24V23H21.6352V24V25H23.5432V24ZM20.9992 20.028L20.1571 20.5673L22.7011 24.5393L23.5432 24L24.3853 23.4607L21.8413 19.4887L20.9992 20.028ZM23.3272 17.28L22.5642 16.6336L20.2362 19.3816L20.9992 20.028L21.7622 20.6744L24.0902 17.9264L23.3272 17.28ZM21.4072 17.28V18.28H23.3272V17.28V16.28H21.4072V17.28ZM19.0792 20.184L19.8594 20.8095L22.1874 17.9055L21.4072 17.28L20.6269 16.6545L18.2989 19.5585L19.0792 20.184ZM19.0312 20.184V21.184H19.0792V20.184V19.184H19.0312V20.184ZM19.0312 14.424H18.0312V20.184H19.0312H20.0312V14.424H19.0312ZM17.3152 14.424V15.424H19.0312V14.424V13.424H17.3152V14.424Z" fill="black" mask="url(#path-2-outside-1_0_3057)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_3057" x="6.77366" y="6.27366" width="26.4527" height="26.4527" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="1.61317"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0235294 0 0 0 0 0.101961 0 0 0 0 0.0784314 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_3057"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_3057" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_3057" x1="20" y1="0" x2="20" y2="38" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#081813"/>
|
||||
<stop offset="0.086884" stop-color="#09271E"/>
|
||||
<stop offset="0.254279" stop-color="#0B3227"/>
|
||||
<stop offset="0.512521" stop-color="#0E3D2F"/>
|
||||
<stop offset="0.800267" stop-color="#0B3227"/>
|
||||
<stop offset="0.897594" stop-color="#0A2B21"/>
|
||||
<stop offset="1" stop-color="#081813"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_0_3057" x1="20" y1="0" x2="20" y2="38" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#08251C"/>
|
||||
<stop offset="0.465449" stop-color="#208769"/>
|
||||
<stop offset="0.844436" stop-color="#08241C"/>
|
||||
<stop offset="1" stop-color="#051612"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,27 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="22.9091" viewBox="0 0 24 22.9091" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="print" clip-path="url(#clip0_0_2981)">
|
||||
<g id="Union" filter="url(#filter0_d_0_2981)">
|
||||
<mask id="path-1-outside-1_0_2981" maskUnits="userSpaceOnUse" x="2" y="2.36328" width="20" height="18" fill="black">
|
||||
<rect fill="white" x="2" y="2.36328" width="20" height="18"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4 7.66328H18.3C19.79 7.66328 21 8.81328 21 10.2433V15.3933H17.4V18.8333H6.6V15.3933H3V10.2433C3 8.81328 4.21 7.66328 5.7 7.66328H6.6V3.36328H17.4V7.66328ZM15.6 5.08328H8.4V7.66328H15.6V5.08328ZM8.4 17.1133H15.6V13.6733H8.4V17.1133ZM17.4 11.9533V13.6733H19.2V10.2333C19.2 9.76328 18.8 9.37328 18.3 9.37328H5.7C5.2 9.37328 4.8 9.76328 4.8 10.2333V13.6733H6.6V11.9533H17.4ZM18.3 10.6633C18.3 11.1382 17.8971 11.5233 17.4 11.5233C16.9029 11.5233 16.5 11.1382 16.5 10.6633C16.5 10.1883 16.9029 9.80328 17.4 9.80328C17.8971 9.80328 18.3 10.1883 18.3 10.6633Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4 7.66328H18.3C19.79 7.66328 21 8.81328 21 10.2433V15.3933H17.4V18.8333H6.6V15.3933H3V10.2433C3 8.81328 4.21 7.66328 5.7 7.66328H6.6V3.36328H17.4V7.66328ZM15.6 5.08328H8.4V7.66328H15.6V5.08328ZM8.4 17.1133H15.6V13.6733H8.4V17.1133ZM17.4 11.9533V13.6733H19.2V10.2333C19.2 9.76328 18.8 9.37328 18.3 9.37328H5.7C5.2 9.37328 4.8 9.76328 4.8 10.2333V13.6733H6.6V11.9533H17.4ZM18.3 10.6633C18.3 11.1382 17.8971 11.5233 17.4 11.5233C16.9029 11.5233 16.5 11.1382 16.5 10.6633C16.5 10.1883 16.9029 9.80328 17.4 9.80328C17.8971 9.80328 18.3 10.1883 18.3 10.6633Z" fill="#FFC600"/>
|
||||
<path d="M17.4 7.66328H16.5V8.56328H17.4V7.66328ZM21 15.3933V16.2933H21.9V15.3933H21ZM17.4 15.3933V14.4933H16.5V15.3933H17.4ZM17.4 18.8333V19.7333H18.3V18.8333H17.4ZM6.6 18.8333H5.7V19.7333H6.6V18.8333ZM6.6 15.3933H7.5V14.4933H6.6V15.3933ZM3 15.3933H2.1V16.2933H3V15.3933ZM6.6 7.66328V8.56328H7.5V7.66328H6.6ZM6.6 3.36328V2.46328H5.7V3.36328H6.6ZM17.4 3.36328H18.3V2.46328H17.4V3.36328ZM8.4 5.08328V4.18328H7.5V5.08328H8.4ZM15.6 5.08328H16.5V4.18328H15.6V5.08328ZM8.4 7.66328H7.5V8.56328H8.4V7.66328ZM15.6 7.66328V8.56328H16.5V7.66328H15.6ZM15.6 17.1133V18.0133H16.5V17.1133H15.6ZM8.4 17.1133H7.5V18.0133H8.4V17.1133ZM15.6 13.6733H16.5V12.7733H15.6V13.6733ZM8.4 13.6733V12.7733H7.5V13.6733H8.4ZM17.4 13.6733H16.5V14.5733H17.4V13.6733ZM17.4 11.9533H18.3V11.0533H17.4V11.9533ZM19.2 13.6733V14.5733H20.1V13.6733H19.2ZM4.8 13.6733H3.9V14.5733H4.8V13.6733ZM6.6 13.6733V14.5733H7.5V13.6733H6.6ZM6.6 11.9533V11.0533H5.7V11.9533H6.6ZM18.3 7.66328V6.76328H17.4V7.66328V8.56328H18.3V7.66328ZM21 10.2433H21.9C21.9 8.27672 20.2466 6.76328 18.3 6.76328V7.66328V8.56328C19.3334 8.56328 20.1 9.34984 20.1 10.2433H21ZM21 15.3933H21.9V10.2433H21H20.1V15.3933H21ZM17.4 15.3933V16.2933H21V15.3933V14.4933H17.4V15.3933ZM17.4 18.8333H18.3V15.3933H17.4H16.5V18.8333H17.4ZM6.6 18.8333V19.7333H17.4V18.8333V17.9333H6.6V18.8333ZM6.6 15.3933H5.7V18.8333H6.6H7.5V15.3933H6.6ZM3 15.3933V16.2933H6.6V15.3933V14.4933H3V15.3933ZM3 10.2433H2.1V15.3933H3H3.9V10.2433H3ZM5.7 7.66328V6.76328C3.75339 6.76328 2.1 8.27672 2.1 10.2433H3H3.9C3.9 9.34984 4.66661 8.56328 5.7 8.56328V7.66328ZM6.6 7.66328V6.76328H5.7V7.66328V8.56328H6.6V7.66328ZM6.6 3.36328H5.7V7.66328H6.6H7.5V3.36328H6.6ZM17.4 3.36328V2.46328H6.6V3.36328V4.26328H17.4V3.36328ZM17.4 7.66328H18.3V3.36328H17.4H16.5V7.66328H17.4ZM8.4 5.08328V5.98328H15.6V5.08328V4.18328H8.4V5.08328ZM8.4 7.66328H9.3V5.08328H8.4H7.5V7.66328H8.4ZM15.6 7.66328V6.76328H8.4V7.66328V8.56328H15.6V7.66328ZM15.6 5.08328H14.7V7.66328H15.6H16.5V5.08328H15.6ZM15.6 17.1133V16.2133H8.4V17.1133V18.0133H15.6V17.1133ZM15.6 13.6733H14.7V17.1133H15.6H16.5V13.6733H15.6ZM8.4 13.6733V14.5733H15.6V13.6733V12.7733H8.4V13.6733ZM8.4 17.1133H9.3V13.6733H8.4H7.5V17.1133H8.4ZM17.4 13.6733H18.3V11.9533H17.4H16.5V13.6733H17.4ZM19.2 13.6733V12.7733H17.4V13.6733V14.5733H19.2V13.6733ZM19.2 10.2333H18.3V13.6733H19.2H20.1V10.2333H19.2ZM18.3 9.37328V10.2733C18.3072 10.2733 18.3108 10.2746 18.3121 10.2751C18.314 10.2759 18.3144 10.2765 18.3137 10.2758C18.313 10.2751 18.3097 10.2717 18.3063 10.2641C18.3026 10.2557 18.3 10.2447 18.3 10.2333H19.2H20.1C20.1 9.23328 19.2635 8.47328 18.3 8.47328V9.37328ZM5.7 9.37328V10.2733H18.3V9.37328V8.47328H5.7V9.37328ZM4.8 10.2333H5.7C5.7 10.2447 5.69736 10.2557 5.69367 10.2641C5.69029 10.2717 5.68701 10.2751 5.6863 10.2758C5.68555 10.2765 5.686 10.2759 5.68789 10.2751C5.68922 10.2746 5.69279 10.2733 5.7 10.2733V9.37328V8.47328C4.73654 8.47328 3.9 9.23328 3.9 10.2333H4.8ZM4.8 13.6733H5.7V10.2333H4.8H3.9V13.6733H4.8ZM6.6 13.6733V12.7733H4.8V13.6733V14.5733H6.6V13.6733ZM6.6 11.9533H5.7V13.6733H6.6H7.5V11.9533H6.6ZM17.4 11.9533V11.0533H6.6V11.9533V12.8533H17.4V11.9533ZM17.4 11.5233V12.4233C18.3551 12.4233 19.2 11.6734 19.2 10.6633H18.3H17.4C17.4 10.6534 17.4024 10.6427 17.4063 10.6337C17.4099 10.6256 17.4136 10.6217 17.4146 10.6207C17.4166 10.6188 17.4134 10.6233 17.4 10.6233V11.5233ZM16.5 10.6633H15.6C15.6 11.6734 16.4449 12.4233 17.4 12.4233V11.5233V10.6233C17.3866 10.6233 17.3834 10.6188 17.3854 10.6207C17.3864 10.6217 17.3901 10.6256 17.3937 10.6337C17.3976 10.6427 17.4 10.6534 17.4 10.6633H16.5ZM17.4 9.80328V8.90328C16.4449 8.90328 15.6 9.65314 15.6 10.6633H16.5H17.4C17.4 10.6732 17.3976 10.6839 17.3937 10.6928C17.3901 10.701 17.3864 10.7048 17.3854 10.7059C17.3834 10.7078 17.3866 10.7033 17.4 10.7033V9.80328ZM18.3 10.6633H19.2C19.2 9.65314 18.3551 8.90328 17.4 8.90328V9.80328V10.7033C17.4134 10.7033 17.4166 10.7078 17.4146 10.7059C17.4136 10.7048 17.4099 10.701 17.4063 10.6928C17.4024 10.6839 17.4 10.6732 17.4 10.6633H18.3Z" fill="black" mask="url(#path-1-outside-1_0_2981)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_2981" x="-1.13" y="-0.766719" width="26.26" height="23.73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="1.615"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0235294 0 0 0 0 0.101961 0 0 0 0 0.0784314 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_2981"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_2981" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_0_2981">
|
||||
<rect width="24" height="22.9091" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,27 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="22.9091" viewBox="0 0 24 22.9091" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="print" clip-path="url(#clip0_0_2981)">
|
||||
<g id="Union" filter="url(#filter0_d_0_2981)">
|
||||
<mask id="path-1-outside-1_0_2981" maskUnits="userSpaceOnUse" x="2" y="2.36328" width="20" height="18" fill="black">
|
||||
<rect fill="white" x="2" y="2.36328" width="20" height="18"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4 7.66328H18.3C19.79 7.66328 21 8.81328 21 10.2433V15.3933H17.4V18.8333H6.6V15.3933H3V10.2433C3 8.81328 4.21 7.66328 5.7 7.66328H6.6V3.36328H17.4V7.66328ZM15.6 5.08328H8.4V7.66328H15.6V5.08328ZM8.4 17.1133H15.6V13.6733H8.4V17.1133ZM17.4 11.9533V13.6733H19.2V10.2333C19.2 9.76328 18.8 9.37328 18.3 9.37328H5.7C5.2 9.37328 4.8 9.76328 4.8 10.2333V13.6733H6.6V11.9533H17.4ZM18.3 10.6633C18.3 11.1382 17.8971 11.5233 17.4 11.5233C16.9029 11.5233 16.5 11.1382 16.5 10.6633C16.5 10.1883 16.9029 9.80328 17.4 9.80328C17.8971 9.80328 18.3 10.1883 18.3 10.6633Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4 7.66328H18.3C19.79 7.66328 21 8.81328 21 10.2433V15.3933H17.4V18.8333H6.6V15.3933H3V10.2433C3 8.81328 4.21 7.66328 5.7 7.66328H6.6V3.36328H17.4V7.66328ZM15.6 5.08328H8.4V7.66328H15.6V5.08328ZM8.4 17.1133H15.6V13.6733H8.4V17.1133ZM17.4 11.9533V13.6733H19.2V10.2333C19.2 9.76328 18.8 9.37328 18.3 9.37328H5.7C5.2 9.37328 4.8 9.76328 4.8 10.2333V13.6733H6.6V11.9533H17.4ZM18.3 10.6633C18.3 11.1382 17.8971 11.5233 17.4 11.5233C16.9029 11.5233 16.5 11.1382 16.5 10.6633C16.5 10.1883 16.9029 9.80328 17.4 9.80328C17.8971 9.80328 18.3 10.1883 18.3 10.6633Z" fill="#B6D0C9"/>
|
||||
<path d="M17.4 7.66328H16.5V8.56328H17.4V7.66328ZM21 15.3933V16.2933H21.9V15.3933H21ZM17.4 15.3933V14.4933H16.5V15.3933H17.4ZM17.4 18.8333V19.7333H18.3V18.8333H17.4ZM6.6 18.8333H5.7V19.7333H6.6V18.8333ZM6.6 15.3933H7.5V14.4933H6.6V15.3933ZM3 15.3933H2.1V16.2933H3V15.3933ZM6.6 7.66328V8.56328H7.5V7.66328H6.6ZM6.6 3.36328V2.46328H5.7V3.36328H6.6ZM17.4 3.36328H18.3V2.46328H17.4V3.36328ZM8.4 5.08328V4.18328H7.5V5.08328H8.4ZM15.6 5.08328H16.5V4.18328H15.6V5.08328ZM8.4 7.66328H7.5V8.56328H8.4V7.66328ZM15.6 7.66328V8.56328H16.5V7.66328H15.6ZM15.6 17.1133V18.0133H16.5V17.1133H15.6ZM8.4 17.1133H7.5V18.0133H8.4V17.1133ZM15.6 13.6733H16.5V12.7733H15.6V13.6733ZM8.4 13.6733V12.7733H7.5V13.6733H8.4ZM17.4 13.6733H16.5V14.5733H17.4V13.6733ZM17.4 11.9533H18.3V11.0533H17.4V11.9533ZM19.2 13.6733V14.5733H20.1V13.6733H19.2ZM4.8 13.6733H3.9V14.5733H4.8V13.6733ZM6.6 13.6733V14.5733H7.5V13.6733H6.6ZM6.6 11.9533V11.0533H5.7V11.9533H6.6ZM18.3 7.66328V6.76328H17.4V7.66328V8.56328H18.3V7.66328ZM21 10.2433H21.9C21.9 8.27672 20.2466 6.76328 18.3 6.76328V7.66328V8.56328C19.3334 8.56328 20.1 9.34984 20.1 10.2433H21ZM21 15.3933H21.9V10.2433H21H20.1V15.3933H21ZM17.4 15.3933V16.2933H21V15.3933V14.4933H17.4V15.3933ZM17.4 18.8333H18.3V15.3933H17.4H16.5V18.8333H17.4ZM6.6 18.8333V19.7333H17.4V18.8333V17.9333H6.6V18.8333ZM6.6 15.3933H5.7V18.8333H6.6H7.5V15.3933H6.6ZM3 15.3933V16.2933H6.6V15.3933V14.4933H3V15.3933ZM3 10.2433H2.1V15.3933H3H3.9V10.2433H3ZM5.7 7.66328V6.76328C3.75339 6.76328 2.1 8.27672 2.1 10.2433H3H3.9C3.9 9.34984 4.66661 8.56328 5.7 8.56328V7.66328ZM6.6 7.66328V6.76328H5.7V7.66328V8.56328H6.6V7.66328ZM6.6 3.36328H5.7V7.66328H6.6H7.5V3.36328H6.6ZM17.4 3.36328V2.46328H6.6V3.36328V4.26328H17.4V3.36328ZM17.4 7.66328H18.3V3.36328H17.4H16.5V7.66328H17.4ZM8.4 5.08328V5.98328H15.6V5.08328V4.18328H8.4V5.08328ZM8.4 7.66328H9.3V5.08328H8.4H7.5V7.66328H8.4ZM15.6 7.66328V6.76328H8.4V7.66328V8.56328H15.6V7.66328ZM15.6 5.08328H14.7V7.66328H15.6H16.5V5.08328H15.6ZM15.6 17.1133V16.2133H8.4V17.1133V18.0133H15.6V17.1133ZM15.6 13.6733H14.7V17.1133H15.6H16.5V13.6733H15.6ZM8.4 13.6733V14.5733H15.6V13.6733V12.7733H8.4V13.6733ZM8.4 17.1133H9.3V13.6733H8.4H7.5V17.1133H8.4ZM17.4 13.6733H18.3V11.9533H17.4H16.5V13.6733H17.4ZM19.2 13.6733V12.7733H17.4V13.6733V14.5733H19.2V13.6733ZM19.2 10.2333H18.3V13.6733H19.2H20.1V10.2333H19.2ZM18.3 9.37328V10.2733C18.3072 10.2733 18.3108 10.2746 18.3121 10.2751C18.314 10.2759 18.3144 10.2765 18.3137 10.2758C18.313 10.2751 18.3097 10.2717 18.3063 10.2641C18.3026 10.2557 18.3 10.2447 18.3 10.2333H19.2H20.1C20.1 9.23328 19.2635 8.47328 18.3 8.47328V9.37328ZM5.7 9.37328V10.2733H18.3V9.37328V8.47328H5.7V9.37328ZM4.8 10.2333H5.7C5.7 10.2447 5.69736 10.2557 5.69367 10.2641C5.69029 10.2717 5.68701 10.2751 5.6863 10.2758C5.68555 10.2765 5.686 10.2759 5.68789 10.2751C5.68922 10.2746 5.69279 10.2733 5.7 10.2733V9.37328V8.47328C4.73654 8.47328 3.9 9.23328 3.9 10.2333H4.8ZM4.8 13.6733H5.7V10.2333H4.8H3.9V13.6733H4.8ZM6.6 13.6733V12.7733H4.8V13.6733V14.5733H6.6V13.6733ZM6.6 11.9533H5.7V13.6733H6.6H7.5V11.9533H6.6ZM17.4 11.9533V11.0533H6.6V11.9533V12.8533H17.4V11.9533ZM17.4 11.5233V12.4233C18.3551 12.4233 19.2 11.6734 19.2 10.6633H18.3H17.4C17.4 10.6534 17.4024 10.6427 17.4063 10.6337C17.4099 10.6256 17.4136 10.6217 17.4146 10.6207C17.4166 10.6188 17.4134 10.6233 17.4 10.6233V11.5233ZM16.5 10.6633H15.6C15.6 11.6734 16.4449 12.4233 17.4 12.4233V11.5233V10.6233C17.3866 10.6233 17.3834 10.6188 17.3854 10.6207C17.3864 10.6217 17.3901 10.6256 17.3937 10.6337C17.3976 10.6427 17.4 10.6534 17.4 10.6633H16.5ZM17.4 9.80328V8.90328C16.4449 8.90328 15.6 9.65314 15.6 10.6633H16.5H17.4C17.4 10.6732 17.3976 10.6839 17.3937 10.6928C17.3901 10.701 17.3864 10.7048 17.3854 10.7059C17.3834 10.7078 17.3866 10.7033 17.4 10.7033V9.80328ZM18.3 10.6633H19.2C19.2 9.65314 18.3551 8.90328 17.4 8.90328V9.80328V10.7033C17.4134 10.7033 17.4166 10.7078 17.4146 10.7059C17.4136 10.7048 17.4099 10.701 17.4063 10.6928C17.4024 10.6839 17.4 10.6732 17.4 10.6633H18.3Z" fill="black" mask="url(#path-1-outside-1_0_2981)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_2981" x="-1.13" y="-0.766719" width="26.26" height="23.73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="1.615"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0235294 0 0 0 0 0.101961 0 0 0 0 0.0784314 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_2981"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_2981" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_0_2981">
|
||||
<rect width="24" height="22.9091" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="208" height="208" viewBox="0 0 208 208" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 2745">
|
||||
<circle id="Ellipse 409" cx="104" cy="104" r="103.5" stroke="#726E65"/>
|
||||
<circle id="Ellipse 410" cx="104" cy="104" r="84" stroke="#161410" stroke-width="2"/>
|
||||
<path id="Vector 923" d="M19.8594 34.2539L29.1703 41.972" stroke="#726E65"/>
|
||||
<path id="Polygon 335" d="M23.6286 38.2515L22.4597 47.059L15.1599 40.9383L23.6286 38.2515Z" fill="#726E65"/>
|
||||
<path id="Polygon 336" d="M23.6677 38.1969L32.1364 35.5101L24.8365 29.3894L23.6677 38.1969Z" fill="#726E65"/>
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M19.8047 103.013L27.3047 107.776V103.75L180.062 103.75V108.25L187.562 103.487L180.062 98.7242V102.75L27.3047 102.75V98.2496L19.8047 103.013Z" fill="#726E65"/>
|
||||
<path id="Union_2" fill-rule="evenodd" clip-rule="evenodd" d="M104 104L108.763 111.5H104.5L104.5 180.5H108.763L104 188L99.2369 180.5H103.5L103.5 111.5H99.2369L104 104Z" fill="#726E65"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,28 @@
|
||||
<svg width="44" height="30" viewBox="0 0 44 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="44" height="30" fill="#F5F5F5"/>
|
||||
<g id="2. 헤더">
|
||||
<path d="M-2243 -4131C-2243 -4132.1 -2242.1 -4133 -2241 -4133H3016C3017.1 -4133 3018 -4132.1 3018 -4131V8497C3018 8498.1 3017.1 8499 3016 8499H-2241C-2242.1 8499 -2243 8498.1 -2243 8497V-4131Z" fill="white"/>
|
||||
<path d="M-2241 -4132H3016V-4134H-2241V-4132ZM3017 -4131V8497H3019V-4131H3017ZM3016 8498H-2241V8500H3016V8498ZM-2242 8497V-4131H-2244V8497H-2242ZM-2241 8498C-2241.55 8498 -2242 8497.55 -2242 8497H-2244C-2244 8498.66 -2242.66 8500 -2241 8500V8498ZM3017 8497C3017 8497.55 3016.55 8498 3016 8498V8500C3017.66 8500 3019 8498.66 3019 8497H3017ZM3016 -4132C3016.55 -4132 3017 -4131.55 3017 -4131H3019C3019 -4132.66 3017.66 -4134 3016 -4134V-4132ZM-2241 -4134C-2242.66 -4134 -2244 -4132.66 -2244 -4131H-2242C-2242 -4131.55 -2241.55 -4132 -2241 -4132V-4134Z" fill="black" fill-opacity="0.1"/>
|
||||
<g id="btn_close popup">
|
||||
<rect x="-19.5" y="-69.5" width="83" height="119" rx="4.5" stroke="#9747FF" stroke-dasharray="10 5"/>
|
||||
<g id="Property 1=Variant2">
|
||||
<g id="Rectangle 7347">
|
||||
<path d="M0 0H41C42.6569 0 44 1.34315 44 3V30H0V0Z" fill="url(#paint0_linear_25_48)"/>
|
||||
<path d="M0 0H41C42.6569 0 44 1.34315 44 3V30H0V0Z" fill="#E40000"/>
|
||||
</g>
|
||||
<g id="Group 2894">
|
||||
<path id="Line 940" d="M26 9L35.0909 18.0909" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path id="Line 941" d="M35.2891 9L26.1982 18.0909" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_25_48" x1="23.6354" y1="0" x2="23.6354" y2="32.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0F3025"/>
|
||||
<stop offset="0.209337" stop-color="#194335"/>
|
||||
<stop offset="0.913092" stop-color="#0B221B"/>
|
||||
<stop offset="1" stop-color="#0D231C"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,43 @@
|
||||
<svg width="44" height="30" viewBox="0 0 44 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="44" height="30" fill="#F5F5F5"/>
|
||||
<g id="2. 헤더">
|
||||
<path d="M-2243 -4081C-2243 -4082.1 -2242.1 -4083 -2241 -4083H3016C3017.1 -4083 3018 -4082.1 3018 -4081V8547C3018 8548.1 3017.1 8549 3016 8549H-2241C-2242.1 8549 -2243 8548.1 -2243 8547V-4081Z" fill="white"/>
|
||||
<path d="M-2241 -4082H3016V-4084H-2241V-4082ZM3017 -4081V8547H3019V-4081H3017ZM3016 8548H-2241V8550H3016V8548ZM-2242 8547V-4081H-2244V8547H-2242ZM-2241 8548C-2241.55 8548 -2242 8547.55 -2242 8547H-2244C-2244 8548.66 -2242.66 8550 -2241 8550V8548ZM3017 8547C3017 8547.55 3016.55 8548 3016 8548V8550C3017.66 8550 3019 8548.66 3019 8547H3017ZM3016 -4082C3016.55 -4082 3017 -4081.55 3017 -4081H3019C3019 -4082.66 3017.66 -4084 3016 -4084V-4082ZM-2241 -4084C-2242.66 -4084 -2244 -4082.66 -2244 -4081H-2242C-2242 -4081.55 -2241.55 -4082 -2241 -4082V-4084Z" fill="black" fill-opacity="0.1"/>
|
||||
<g id="btn_close popup">
|
||||
<rect x="-19.5" y="-19.5" width="83" height="119" rx="4.5" stroke="#9747FF" stroke-dasharray="10 5"/>
|
||||
<g id="Property 1=Default">
|
||||
<g id="Rectangle 7347">
|
||||
<path d="M41 0.5C42.3807 0.5 43.5 1.61929 43.5 3V29.5H0.5V0.5H41Z" fill="url(#paint0_linear_25_48)"/>
|
||||
<path d="M41 0.5C42.3807 0.5 43.5 1.61929 43.5 3V29.5H0.5V0.5H41Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M41 0.5C42.3807 0.5 43.5 1.61929 43.5 3V29.5H0.5V0.5H41Z" stroke="url(#paint1_linear_25_48)"/>
|
||||
</g>
|
||||
<g id="Group 2894" filter="url(#filter0_d_25_48)">
|
||||
<path id="Line 940" d="M26 9L35.0909 18.0909" stroke="#969696" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path id="Line 941" d="M35.2891 9L26.1982 18.0909" stroke="#969696" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_25_48" x="21.25" y="4.25" width="18.7891" height="18.5918" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_25_48"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_25_48" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_25_48" x1="23.6354" y1="0" x2="23.6354" y2="32.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0F3025"/>
|
||||
<stop offset="0.209337" stop-color="#194335"/>
|
||||
<stop offset="0.913092" stop-color="#0B221B"/>
|
||||
<stop offset="1" stop-color="#0D231C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_25_48" x1="33.5" y1="25.3484" x2="33.5" y2="1.61974e-07" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#154838"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="296" height="12" viewBox="0 0 296 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="btn_view more">
|
||||
<path id="Polygon 357" opacity="0.5" d="M148 8.5L151.031 4.75H144.969L148 8.5Z" fill="#26221A"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
@@ -0,0 +1,31 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="317" height="36.0847" viewBox="0 0 317 36.0847" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Union" filter="url(#filter0_d_0_137)">
|
||||
<mask id="path-1-inside-1_0_137" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M265 6H313C306.648 7.28392 302.05 14.3788 297.685 21.1155C293.369 27.775 289.28 34.0846 283.947 34.0846L266 34.0847H8C5.79087 34.0847 4 32.2939 4 30.0847V9.00012C4 7.34327 5.34315 6.00012 7 6.00012H265V6Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M265 6H313C306.648 7.28392 302.05 14.3788 297.685 21.1155C293.369 27.775 289.28 34.0846 283.947 34.0846L266 34.0847H8C5.79087 34.0847 4 32.2939 4 30.0847V9.00012C4 7.34327 5.34315 6.00012 7 6.00012H265V6Z" fill="url(#paint0_linear_0_137)"/>
|
||||
<path d="M313 6L313.198 6.98018L313 5V6ZM265 6V5H264V6H265ZM297.685 21.1155L298.524 21.6593V21.6593L297.685 21.1155ZM283.947 34.0846V33.0846V33.0846V34.0846ZM266 34.0847V35.0847V35.0847V34.0847ZM265 6.00012V7.00012H266V6.00012H265ZM313 6V5H265V6V7H313V6ZM297.685 21.1155L298.524 21.6593C300.717 18.2754 302.915 14.8877 305.342 12.1805C307.771 9.4715 310.329 7.56003 313.198 6.98018L313 6L312.802 5.01982C309.319 5.72389 306.402 8.0018 303.853 10.8454C301.302 13.6908 299.018 17.2188 296.845 20.5716L297.685 21.1155ZM283.947 34.0846V35.0846C287.035 35.0846 289.64 33.255 291.932 30.7763C294.23 28.2904 296.38 24.9677 298.524 21.6593L297.685 21.1155L296.845 20.5716C294.674 23.9227 292.621 27.0846 290.463 29.4185C288.299 31.7594 286.192 33.0846 283.947 33.0846V34.0846ZM266 34.0847V35.0847L283.947 35.0846V34.0846V33.0846L266 33.0847V34.0847ZM266 34.0847V33.0847H8V34.0847V35.0847H266V34.0847ZM8 34.0847V33.0847C6.34315 33.0847 5 31.7416 5 30.0847H4H3C3 32.8461 5.23858 35.0847 8 35.0847V34.0847ZM4 30.0847H5V9.00012H4H3V30.0847H4ZM4 9.00012H5C5 7.89555 5.89543 7.00012 7 7.00012V6.00012V5.00012C4.79086 5.00012 3 6.79098 3 9.00012H4ZM7 6.00012V7.00012H265V6.00012V5.00012H7V6.00012ZM265 6H264V6.00012H265H266V6H265Z" fill="url(#paint1_linear_0_137)" mask="url(#path-1-inside-1_0_137)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_137" x="0" y="0" width="317" height="36.0847" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="-2"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_137"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_137" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_137" x1="169.245" y1="26.4913" x2="169.245" y2="52.571" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0F3025"/>
|
||||
<stop offset="0.209337" stop-color="#194335"/>
|
||||
<stop offset="0.913092" stop-color="#0B221B"/>
|
||||
<stop offset="1" stop-color="#0D231C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_0_137" x1="186.108" y1="26.4913" x2="186.108" y2="52.571" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#154838"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,28 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="308" height="38" viewBox="0 0 308 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Union" filter="url(#filter0_d_0_140)">
|
||||
<mask id="path-1-inside-1_0_140" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M252 6H0V33C0 34.6569 1.34314 36 3 36H252H260H270.947C276.28 36 280.369 29.2601 284.685 22.1464C289.05 14.9502 293.648 7.37149 300 6H260H252Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M252 6H0V33C0 34.6569 1.34314 36 3 36H252H260H270.947C276.28 36 280.369 29.2601 284.685 22.1464C289.05 14.9502 293.648 7.37149 300 6H260H252Z" fill="#211D13"/>
|
||||
<path d="M0 6V5H-1V6H0ZM284.685 22.1464L285.54 22.665V22.665L284.685 22.1464ZM300 6L300.211 6.97748L300 5V6ZM0 6V7H252V6V5H0V6ZM0 33H1V6H0H-1V33H0ZM3 36V35C1.89543 35 1 34.1046 1 33H0H-1C-1 35.2091 0.790855 37 3 37V36ZM252 36V35H3V36V37H252V36ZM252 36V37H260V36V35H252V36ZM260 36V37H270.947V36V35H260V36ZM270.947 36V37C274.062 37 276.672 35.0311 278.953 32.3953C281.248 29.7442 283.395 26.2 285.54 22.665L284.685 22.1464L283.83 21.6277C281.658 25.2064 279.603 28.5891 277.441 31.0864C275.266 33.5989 273.165 35 270.947 35V36ZM284.685 22.1464L285.54 22.665C287.732 19.051 289.933 15.4276 292.363 12.5318C294.799 9.62962 297.357 7.59367 300.211 6.97748L300 6L299.789 5.02252C296.291 5.77782 293.374 8.21697 290.831 11.246C288.284 14.2816 286.003 18.0456 283.83 21.6277L284.685 22.1464ZM300 6V5H260V6V7H300V6ZM260 6V5H252V6V7H260V6Z" fill="url(#paint0_linear_0_140)" mask="url(#path-1-inside-1_0_140)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_140" x="0" y="0" width="308" height="38" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="4" dy="-2"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_140"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_140" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_140" x1="150" y1="6" x2="150" y2="36" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#493927"/>
|
||||
<stop offset="0.17" stop-color="#6F5739"/>
|
||||
<stop offset="0.505" stop-color="#A38663"/>
|
||||
<stop offset="0.849271" stop-color="#2C2213"/>
|
||||
<stop offset="1" stop-color="#413A2B"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,32 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="308" height="38" viewBox="0 0 308 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Union" filter="url(#filter0_d_0_167)">
|
||||
<mask id="path-1-inside-1_0_167" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M252 6H0V33C0 34.6569 1.34314 36 3 36H252H260H270.947C276.28 36 280.369 29.2601 284.685 22.1464C289.05 14.9502 293.648 7.37149 300 6H260H252Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M252 6H0V33C0 34.6569 1.34314 36 3 36H252H260H270.947C276.28 36 280.369 29.2601 284.685 22.1464C289.05 14.9502 293.648 7.37149 300 6H260H252Z" fill="url(#paint0_linear_0_167)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M252 6H0V33C0 34.6569 1.34314 36 3 36H252H260H270.947C276.28 36 280.369 29.2601 284.685 22.1464C289.05 14.9502 293.648 7.37149 300 6H260H252Z" fill="black" fill-opacity="0.2"/>
|
||||
<path d="M0 6V5H-1V6H0ZM284.685 22.1464L285.54 22.665V22.665L284.685 22.1464ZM300 6L300.211 6.97748L300 5V6ZM0 6V7H252V6V5H0V6ZM0 33H1V6H0H-1V33H0ZM3 36V35C1.89543 35 1 34.1046 1 33H0H-1C-1 35.2091 0.790855 37 3 37V36ZM252 36V35H3V36V37H252V36ZM252 36V37H260V36V35H252V36ZM260 36V37H270.947V36V35H260V36ZM270.947 36V37C274.062 37 276.672 35.0311 278.953 32.3953C281.248 29.7442 283.395 26.2 285.54 22.665L284.685 22.1464L283.83 21.6277C281.658 25.2064 279.603 28.5891 277.441 31.0864C275.266 33.5989 273.165 35 270.947 35V36ZM284.685 22.1464L285.54 22.665C287.732 19.051 289.933 15.4276 292.363 12.5318C294.799 9.62962 297.357 7.59367 300.211 6.97748L300 6L299.789 5.02252C296.291 5.77782 293.374 8.21697 290.831 11.246C288.284 14.2816 286.003 18.0456 283.83 21.6277L284.685 22.1464ZM300 6V5H260V6V7H300V6ZM260 6V5H252V6V7H260V6Z" fill="url(#paint1_linear_0_167)" mask="url(#path-1-inside-1_0_167)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_0_167" x="0" y="0" width="308" height="38" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="4" dy="-2"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_167"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_167" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_0_167" x1="161.151" y1="6" x2="161.151" y2="38.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0F3025"/>
|
||||
<stop offset="0.34535" stop-color="#194335"/>
|
||||
<stop offset="0.913092" stop-color="#0B221B"/>
|
||||
<stop offset="1" stop-color="#0D231C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_0_167" x1="178.023" y1="6" x2="178.023" y2="38.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#154838"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,74 @@
|
||||
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="29" height="28" fill="#F5F5F5"/>
|
||||
<g id="2. 헤더">
|
||||
<path d="M-2585 -4174.26C-2585 -4175.36 -2584.1 -4176.26 -2583 -4176.26H2674C2675.1 -4176.26 2676 -4175.36 2676 -4174.26V8453.74C2676 8454.85 2675.1 8455.74 2674 8455.74H-2583C-2584.1 8455.74 -2585 8454.85 -2585 8453.74V-4174.26Z" fill="white"/>
|
||||
<path d="M-2583 -4175.26H2674V-4177.26H-2583V-4175.26ZM2675 -4174.26V8453.74H2677V-4174.26H2675ZM2674 8454.74H-2583V8456.74H2674V8454.74ZM-2584 8453.74V-4174.26H-2586V8453.74H-2584ZM-2583 8454.74C-2583.55 8454.74 -2584 8454.29 -2584 8453.74H-2586C-2586 8455.4 -2584.66 8456.74 -2583 8456.74V8454.74ZM2675 8453.74C2675 8454.29 2674.55 8454.74 2674 8454.74V8456.74C2675.66 8456.74 2677 8455.4 2677 8453.74H2675ZM2674 -4175.26C2674.55 -4175.26 2675 -4174.81 2675 -4174.26H2677C2677 -4175.91 2675.66 -4177.26 2674 -4177.26V-4175.26ZM-2583 -4177.26C-2584.66 -4177.26 -2586 -4175.92 -2586 -4174.26H-2584C-2584 -4174.81 -2583.55 -4175.26 -2583 -4175.26V-4177.26Z" fill="black" fill-opacity="0.1"/>
|
||||
<g id="Group 427322294">
|
||||
<rect x="-19.5" y="-113.758" width="68" height="160.387" rx="4.5" stroke="#9747FF" stroke-dasharray="10 5"/>
|
||||
<g id="Property 1=Variant3">
|
||||
<g id="Group 3815">
|
||||
<g id="Group 3789">
|
||||
<g id="Group 3788">
|
||||
<g id="Rectangle 6959">
|
||||
<g filter="url(#filter0_i_25_48)">
|
||||
<rect width="29" height="27.129" rx="3" fill="black" fill-opacity="0.8"/>
|
||||
</g>
|
||||
<rect x="0.5" y="0.5" width="28" height="26.129" rx="2.5" stroke="url(#paint0_linear_25_48)"/>
|
||||
</g>
|
||||
<g id="Group 3787">
|
||||
<g id="Group 3781">
|
||||
<g id="Component 743">
|
||||
<rect id="Rectangle 6960" x="2.37109" y="2.36719" width="24.2581" height="22.3871" rx="1.5" fill="url(#paint1_linear_25_48)" stroke="url(#paint2_linear_25_48)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Vector" d="M11.6957 5L5 8.86435V16.44L11.6957 20.3043L18.3913 16.44V8.86435L11.6957 5ZM10.7391 17.54L6.91304 15.34V10.8443L10.7391 13.0539V17.54ZM8.07043 9.30435L11.6957 7.20957L15.3304 9.30435L11.6957 11.3991L8.07043 9.30435ZM16.4783 15.34L12.6522 17.54V13.0539L16.4783 10.8443V15.34Z" fill="#FFC600" stroke="black" stroke-miterlimit="10"/>
|
||||
<g id="Group 3780">
|
||||
<g id="Group 3782">
|
||||
<g id="Group 3779">
|
||||
<g id="Group">
|
||||
<path id="Vector_2" d="M24.4202 15.8162L15.1842 9.18308L17.0539 20.7914L19.1947 18.0907L21.7913 22.3464L23.7959 20.9877L21.1992 16.7321L24.4202 15.8162Z" fill="#FFC600"/>
|
||||
<path id="Vector_3" d="M21.8936 22.9544C21.6725 22.9962 21.4276 22.8983 21.2995 22.6823L19.1337 19.1331L17.5037 21.1897C17.361 21.3703 17.1286 21.4527 16.899 21.3904C16.6781 21.3264 16.509 21.1374 16.4782 20.903L14.6085 9.29468C14.5673 9.05267 14.6707 8.80256 14.8686 8.6787C15.065 8.54553 15.3205 8.54529 15.5098 8.69207L24.7457 15.3251C24.9334 15.4626 25.0278 15.6946 24.9951 15.9314C24.9625 16.1681 24.7949 16.3631 24.577 16.4235L22.1231 17.1272L24.289 20.6764C24.3763 20.8137 24.4048 20.9812 24.3683 21.1418C24.3318 21.3024 24.2464 21.4435 24.1144 21.5261L22.1099 22.8847C22.0439 22.926 21.9675 22.9597 21.8967 22.973L21.8936 22.9544ZM19.0906 17.4888C19.1348 17.4804 19.179 17.472 19.2264 17.4823C19.4112 17.4954 19.5837 17.5974 19.6846 17.7608L21.965 21.4998L22.9878 20.8069L20.7074 17.068C20.6065 16.9045 20.5893 16.6964 20.6492 16.5122C20.7179 16.3263 20.8596 16.1938 21.0421 16.1401L23.0353 15.5714L15.9743 10.5066L17.4039 19.3734L18.7253 17.7019C18.8156 17.5887 18.9403 17.5171 19.0729 17.4921L19.0906 17.4888Z" fill="black"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_25_48" x="0" y="0" width="29" height="29.1289" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="2"/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_25_48"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_25_48" x1="14.5" y1="0" x2="14.5" y2="31.1984" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#092920"/>
|
||||
<stop offset="1" stop-color="#1C6F56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_25_48" x1="14.5001" y1="1.86719" x2="14.5001" y2="25.2543" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#081813"/>
|
||||
<stop offset="0.086884" stop-color="#09271E"/>
|
||||
<stop offset="0.254279" stop-color="#0B3227"/>
|
||||
<stop offset="0.512521" stop-color="#0E3D2F"/>
|
||||
<stop offset="0.800267" stop-color="#0B3227"/>
|
||||
<stop offset="0.897594" stop-color="#0A2B21"/>
|
||||
<stop offset="1" stop-color="#081813"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_25_48" x1="14.5001" y1="1.86719" x2="14.5001" y2="25.2543" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#A67D03"/>
|
||||
<stop offset="0.164109" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.371922" stop-color="#FADD4F"/>
|
||||
<stop offset="0.620285" stop-color="#F2CC0F"/>
|
||||
<stop offset="0.878785" stop-color="#926E03"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,73 @@
|
||||
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="29" height="28" fill="#F5F5F5"/>
|
||||
<g id="2. 헤더">
|
||||
<path d="M-2585 -4127.13C-2585 -4128.24 -2584.1 -4129.13 -2583 -4129.13H2674C2675.1 -4129.13 2676 -4128.24 2676 -4127.13V8500.87C2676 8501.97 2675.1 8502.87 2674 8502.87H-2583C-2584.1 8502.87 -2585 8501.97 -2585 8500.87V-4127.13Z" fill="white"/>
|
||||
<path d="M-2583 -4128.13H2674V-4130.13H-2583V-4128.13ZM2675 -4127.13V8500.87H2677V-4127.13H2675ZM2674 8501.87H-2583V8503.87H2674V8501.87ZM-2584 8500.87V-4127.13H-2586V8500.87H-2584ZM-2583 8501.87C-2583.55 8501.87 -2584 8501.42 -2584 8500.87H-2586C-2586 8502.52 -2584.66 8503.87 -2583 8503.87V8501.87ZM2675 8500.87C2675 8501.42 2674.55 8501.87 2674 8501.87V8503.87C2675.66 8503.87 2677 8502.52 2677 8500.87H2675ZM2674 -4128.13C2674.55 -4128.13 2675 -4127.69 2675 -4127.13H2677C2677 -4128.79 2675.66 -4130.13 2674 -4130.13V-4128.13ZM-2583 -4130.13C-2584.66 -4130.13 -2586 -4128.79 -2586 -4127.13H-2584C-2584 -4127.69 -2583.55 -4128.13 -2583 -4128.13V-4130.13Z" fill="black" fill-opacity="0.1"/>
|
||||
<g id="Group 427322294">
|
||||
<rect x="-19.5" y="-66.6328" width="68" height="160.387" rx="4.5" stroke="#9747FF" stroke-dasharray="10 5"/>
|
||||
<g id="Property 1=Variant2">
|
||||
<g id="Group 3815">
|
||||
<g id="Group 3789">
|
||||
<g id="Group 3788">
|
||||
<g id="Rectangle 6959">
|
||||
<g filter="url(#filter0_i_25_48)">
|
||||
<rect width="29" height="27.129" rx="3" fill="black" fill-opacity="0.8"/>
|
||||
</g>
|
||||
<rect x="0.5" y="0.5" width="28" height="26.129" rx="2.5" stroke="url(#paint0_linear_25_48)"/>
|
||||
</g>
|
||||
<g id="Group 3787">
|
||||
<g id="Group 3781">
|
||||
<g id="Component 743">
|
||||
<rect id="Rectangle 6960" x="2.37109" y="2.36719" width="24.2581" height="22.3871" rx="1.5" fill="url(#paint1_linear_25_48)" stroke="url(#paint2_linear_25_48)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Vector" d="M11.6957 5L5 8.86435V16.44L11.6957 20.3043L18.3913 16.44V8.86435L11.6957 5ZM10.7391 17.54L6.91304 15.34V10.8443L10.7391 13.0539V17.54ZM8.07043 9.30435L11.6957 7.20957L15.3304 9.30435L11.6957 11.3991L8.07043 9.30435ZM16.4783 15.34L12.6522 17.54V13.0539L16.4783 10.8443V15.34Z" fill="#FFC600" stroke="black" stroke-miterlimit="10"/>
|
||||
<g id="Group 3780">
|
||||
<g id="Group 3782">
|
||||
<g id="Group 3779">
|
||||
<g id="Group">
|
||||
<path id="Vector_2" d="M24.4202 15.8162L15.1842 9.18308L17.0539 20.7914L19.1947 18.0907L21.7913 22.3464L23.7959 20.9877L21.1992 16.7321L24.4202 15.8162Z" fill="#FFC600"/>
|
||||
<path id="Vector_3" d="M21.8936 22.9544C21.6725 22.9962 21.4276 22.8983 21.2995 22.6823L19.1337 19.1331L17.5037 21.1897C17.361 21.3703 17.1286 21.4527 16.899 21.3904C16.6781 21.3264 16.509 21.1374 16.4782 20.903L14.6085 9.29468C14.5673 9.05267 14.6707 8.80256 14.8686 8.6787C15.065 8.54553 15.3205 8.54529 15.5098 8.69207L24.7457 15.3251C24.9334 15.4626 25.0278 15.6946 24.9951 15.9314C24.9625 16.1681 24.7949 16.3631 24.577 16.4235L22.1231 17.1272L24.289 20.6764C24.3763 20.8137 24.4048 20.9812 24.3683 21.1418C24.3318 21.3024 24.2464 21.4435 24.1144 21.5261L22.1099 22.8847C22.0439 22.926 21.9675 22.9597 21.8967 22.973L21.8936 22.9544ZM19.0906 17.4888C19.1348 17.4804 19.179 17.472 19.2264 17.4823C19.4112 17.4954 19.5837 17.5974 19.6846 17.7608L21.965 21.4998L22.9878 20.8069L20.7074 17.068C20.6065 16.9045 20.5893 16.6964 20.6492 16.5122C20.7179 16.3263 20.8596 16.1938 21.0421 16.1401L23.0353 15.5714L15.9743 10.5066L17.4039 19.3734L18.7253 17.7019C18.8156 17.5887 18.9403 17.5171 19.0729 17.4921L19.0906 17.4888Z" fill="black"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_25_48" x="0" y="0" width="29" height="29.1289" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="2"/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_25_48"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_25_48" x1="14.5" y1="0" x2="14.5" y2="31.1984" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#092920"/>
|
||||
<stop offset="1" stop-color="#1C6F56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_25_48" x1="14.5001" y1="1.86719" x2="14.5001" y2="25.2543" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#081813"/>
|
||||
<stop offset="0.086884" stop-color="#09271E"/>
|
||||
<stop offset="0.254279" stop-color="#0B3227"/>
|
||||
<stop offset="0.512521" stop-color="#0E3D2F"/>
|
||||
<stop offset="0.800267" stop-color="#0B3227"/>
|
||||
<stop offset="0.897594" stop-color="#0A2B21"/>
|
||||
<stop offset="1" stop-color="#081813"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_25_48" x1="14.5001" y1="1.86719" x2="14.5001" y2="25.2543" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#08251C"/>
|
||||
<stop offset="0.465449" stop-color="#208769"/>
|
||||
<stop offset="0.844436" stop-color="#08241C"/>
|
||||
<stop offset="1" stop-color="#051612"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,73 @@
|
||||
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="29" height="28" fill="#F5F5F5"/>
|
||||
<g id="2. 헤더">
|
||||
<path d="M-2585 -4080C-2585 -4081.1 -2584.1 -4082 -2583 -4082H2674C2675.1 -4082 2676 -4081.1 2676 -4080V8548C2676 8549.1 2675.1 8550 2674 8550H-2583C-2584.1 8550 -2585 8549.1 -2585 8548V-4080Z" fill="white"/>
|
||||
<path d="M-2583 -4081H2674V-4083H-2583V-4081ZM2675 -4080V8548H2677V-4080H2675ZM2674 8549H-2583V8551H2674V8549ZM-2584 8548V-4080H-2586V8548H-2584ZM-2583 8549C-2583.55 8549 -2584 8548.55 -2584 8548H-2586C-2586 8549.66 -2584.66 8551 -2583 8551V8549ZM2675 8548C2675 8548.55 2674.55 8549 2674 8549V8551C2675.66 8551 2677 8549.66 2677 8548H2675ZM2674 -4081C2674.55 -4081 2675 -4080.55 2675 -4080H2677C2677 -4081.66 2675.66 -4083 2674 -4083V-4081ZM-2583 -4083C-2584.66 -4083 -2586 -4081.66 -2586 -4080H-2584C-2584 -4080.55 -2583.55 -4081 -2583 -4081V-4083Z" fill="black" fill-opacity="0.1"/>
|
||||
<g id="Group 427322294">
|
||||
<rect x="-19.5" y="-19.5" width="68" height="160.387" rx="4.5" stroke="#9747FF" stroke-dasharray="10 5"/>
|
||||
<g id="Property 1=Default">
|
||||
<g id="Group 3815">
|
||||
<g id="Group 3789">
|
||||
<g id="Group 3788">
|
||||
<g id="Rectangle 6959">
|
||||
<g filter="url(#filter0_i_25_48)">
|
||||
<rect width="29" height="27.129" rx="3" fill="black" fill-opacity="0.8"/>
|
||||
</g>
|
||||
<rect x="0.5" y="0.5" width="28" height="26.129" rx="2.5" stroke="url(#paint0_linear_25_48)"/>
|
||||
</g>
|
||||
<g id="Group 3787">
|
||||
<g id="Group 3781">
|
||||
<g id="Component 743">
|
||||
<rect id="Rectangle 6960" x="2.37109" y="2.36719" width="24.2581" height="22.3871" rx="1.5" fill="url(#paint1_linear_25_48)" stroke="url(#paint2_linear_25_48)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Vector" d="M11.6957 5L5 8.86435V16.44L11.6957 20.3043L18.3913 16.44V8.86435L11.6957 5ZM10.7391 17.54L6.91304 15.34V10.8443L10.7391 13.0539V17.54ZM8.07043 9.30435L11.6957 7.20957L15.3304 9.30435L11.6957 11.3991L8.07043 9.30435ZM16.4783 15.34L12.6522 17.54V13.0539L16.4783 10.8443V15.34Z" fill="#B6D0C9" stroke="black" stroke-miterlimit="10"/>
|
||||
<g id="Group 3780">
|
||||
<g id="Group 3782">
|
||||
<g id="Group 3779">
|
||||
<g id="Group">
|
||||
<path id="Vector_2" d="M24.4202 15.8162L15.1842 9.18308L17.0539 20.7914L19.1947 18.0907L21.7913 22.3464L23.7959 20.9877L21.1992 16.7321L24.4202 15.8162Z" fill="#B6D0C9"/>
|
||||
<path id="Vector_3" d="M21.8936 22.9544C21.6725 22.9962 21.4276 22.8983 21.2995 22.6823L19.1337 19.1331L17.5037 21.1897C17.361 21.3703 17.1286 21.4527 16.899 21.3904C16.6781 21.3264 16.509 21.1374 16.4782 20.903L14.6085 9.29468C14.5673 9.05267 14.6707 8.80256 14.8686 8.6787C15.065 8.54553 15.3205 8.54529 15.5098 8.69207L24.7457 15.3251C24.9334 15.4626 25.0278 15.6946 24.9951 15.9314C24.9625 16.1681 24.7949 16.3631 24.577 16.4235L22.1231 17.1272L24.289 20.6764C24.3763 20.8137 24.4048 20.9812 24.3683 21.1418C24.3318 21.3024 24.2464 21.4435 24.1144 21.5261L22.1099 22.8847C22.0439 22.926 21.9675 22.9597 21.8967 22.973L21.8936 22.9544ZM19.0906 17.4888C19.1348 17.4804 19.179 17.472 19.2264 17.4823C19.4112 17.4954 19.5837 17.5974 19.6846 17.7608L21.965 21.4998L22.9878 20.8069L20.7074 17.068C20.6065 16.9045 20.5893 16.6964 20.6492 16.5122C20.7179 16.3263 20.8596 16.1938 21.0421 16.1401L23.0353 15.5714L15.9743 10.5066L17.4039 19.3734L18.7253 17.7019C18.8156 17.5887 18.9403 17.5171 19.0729 17.4921L19.0906 17.4888Z" fill="black"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_25_48" x="0" y="0" width="29" height="29.1289" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="2"/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_25_48"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_25_48" x1="14.5" y1="0" x2="14.5" y2="31.1984" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#092920"/>
|
||||
<stop offset="1" stop-color="#1C6F56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_25_48" x1="14.5001" y1="1.86719" x2="14.5001" y2="25.2543" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#081813"/>
|
||||
<stop offset="0.086884" stop-color="#09271E"/>
|
||||
<stop offset="0.254279" stop-color="#0B3227"/>
|
||||
<stop offset="0.512521" stop-color="#0E3D2F"/>
|
||||
<stop offset="0.800267" stop-color="#0B3227"/>
|
||||
<stop offset="0.897594" stop-color="#0A2B21"/>
|
||||
<stop offset="1" stop-color="#081813"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_25_48" x1="14.5001" y1="1.86719" x2="14.5001" y2="25.2543" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#08251C"/>
|
||||
<stop offset="0.465449" stop-color="#208769"/>
|
||||
<stop offset="0.844436" stop-color="#08241C"/>
|
||||
<stop offset="1" stop-color="#051612"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="13.0291" height="16.7588" viewBox="0 0 13.0291 16.7588" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_6" maskUnits="userSpaceOnUse" x="-0.992468" y="0" width="15" height="17" fill="black">
|
||||
<rect fill="white" x="-0.992468" width="15" height="17"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.69338 2.11056C6.61968 1.96315 6.40932 1.96315 6.33561 2.11056L2.0294 10.723C1.94597 10.8898 2.11623 11.07 2.28752 10.9961L6.03315 9.37989V14.5588C6.03315 14.6693 6.12269 14.7588 6.23315 14.7588H6.92474C7.03519 14.7588 7.12474 14.6693 7.12474 14.5588V9.4364L10.7416 10.9962C10.9129 11.0701 11.0831 10.89 10.9997 10.7231L6.69338 2.11056Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.69338 2.11056C6.61968 1.96315 6.40932 1.96315 6.33561 2.11056L2.0294 10.723C1.94597 10.8898 2.11623 11.07 2.28752 10.9961L6.03315 9.37989V14.5588C6.03315 14.6693 6.12269 14.7588 6.23315 14.7588H6.92474C7.03519 14.7588 7.12474 14.6693 7.12474 14.5588V9.4364L10.7416 10.9962C10.9129 11.0701 11.0831 10.89 10.9997 10.7231L6.69338 2.11056Z" fill="#1C6A51"/>
|
||||
<path d="M6.33561 2.11056L4.54676 1.21613L4.54676 1.21613L6.33561 2.11056ZM6.69338 2.11056L4.90453 3.00498L4.90453 3.00498L6.69338 2.11056ZM2.0294 10.723L3.81825 11.6174L3.81825 11.6174L2.0294 10.723ZM2.28752 10.9961L1.49517 9.15971H1.49517L2.28752 10.9961ZM6.03315 9.37989H8.03315V6.3387L5.2408 7.54354L6.03315 9.37989ZM7.12474 9.4364L7.91675 7.5999L5.12474 6.39581V9.4364H7.12474ZM10.7416 10.9962L9.94956 12.8327H9.94956L10.7416 10.9962ZM10.9997 10.7231L9.21081 11.6175L9.21081 11.6176L10.9997 10.7231ZM6.33561 2.11056L8.12447 3.00499C7.46112 4.33167 5.56787 4.33167 4.90453 3.00498L6.69338 2.11056L8.48224 1.21613C7.67149 -0.405373 5.35752 -0.40538 4.54676 1.21613L6.33561 2.11056ZM2.0294 10.723L3.81825 11.6174L8.12447 3.00498L6.33561 2.11056L4.54676 1.21613L0.240545 9.82856L2.0294 10.723ZM2.28752 10.9961L1.49517 9.15971C3.03678 8.49454 4.56914 10.1156 3.81825 11.6174L2.0294 10.723L0.240545 9.82856C-0.677198 11.664 1.19568 13.6454 3.07987 12.8324L2.28752 10.9961ZM6.03315 9.37989L5.2408 7.54354L1.49517 9.15971L2.28752 10.9961L3.07987 12.8324L6.8255 11.2162L6.03315 9.37989ZM6.03315 14.5588H8.03315V9.37989H6.03315H4.03315V14.5588H6.03315ZM6.23315 14.7588V12.7588C7.22726 12.7588 8.03315 13.5647 8.03315 14.5588H6.03315H4.03315C4.03315 15.7738 5.01813 16.7588 6.23315 16.7588V14.7588ZM6.92474 14.7588V12.7588H6.23315V14.7588V16.7588H6.92474V14.7588ZM7.12474 14.5588H5.12474C5.12474 13.5647 5.93064 12.7588 6.92474 12.7588V14.7588V16.7588C8.13975 16.7588 9.12474 15.7738 9.12474 14.5588H7.12474ZM7.12474 9.4364H5.12474V14.5588H7.12474H9.12474V9.4364H7.12474ZM10.7416 10.9962L11.5336 9.15972L7.91675 7.5999L7.12474 9.4364L6.33272 11.2729L9.94956 12.8327L10.7416 10.9962ZM10.9997 10.7231L9.21081 11.6176C8.46001 10.1159 9.992 8.49489 11.5336 9.15972L10.7416 10.9962L9.94956 12.8327C11.8337 13.6453 13.7062 11.664 12.7885 9.82869L10.9997 10.7231ZM6.69338 2.11056L4.90453 3.00498L9.21081 11.6175L10.9997 10.7231L12.7885 9.8287L8.48224 1.21613L6.69338 2.11056Z" fill="black" mask="url(#path-1-outside-1_0_6)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,16 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="28" height="22.8574" viewBox="0 0 28 22.8574" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 2901">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_9" maskUnits="userSpaceOnUse" x="0" y="0" width="28" height="23" fill="black">
|
||||
<rect fill="white" width="28" height="23"/>
|
||||
<path d="M4 18.8574H24V4H4V18.8574ZM26 19.8574L25.9951 19.959C25.9475 20.4297 25.5732 20.8036 25.1025 20.8516L25 20.8574H3L2.89746 20.8516C2.42684 20.8036 2.05248 20.4297 2.00488 19.959L2 19.8574V3C2 2.48232 2.39333 2.05621 2.89746 2.00488L3 2H25L25.1025 2.00488C25.6067 2.05621 26 2.48232 26 3V19.8574Z"/>
|
||||
</mask>
|
||||
<path d="M4 18.8574H24V4H4V18.8574ZM26 19.8574L25.9951 19.959C25.9475 20.4297 25.5732 20.8036 25.1025 20.8516L25 20.8574H3L2.89746 20.8516C2.42684 20.8036 2.05248 20.4297 2.00488 19.959L2 19.8574V3C2 2.48232 2.39333 2.05621 2.89746 2.00488L3 2H25L25.1025 2.00488C25.6067 2.05621 26 2.48232 26 3V19.8574Z" fill="#1C6A51"/>
|
||||
<path d="M4 18.8574H2V20.8574H4V18.8574ZM24 18.8574V20.8574H26V18.8574H24ZM24 4H26V2H24V4ZM4 4V2H2V4H4ZM26 19.8574L27.9977 19.9535L28 19.9055V19.8574H26ZM25.9951 19.959L27.985 20.1602L27.9903 20.1077L27.9928 20.055L25.9951 19.959ZM25.1025 20.8516L25.2166 22.8483L25.261 22.8458L25.3053 22.8413L25.1025 20.8516ZM25 20.8574V22.8574H25.0571L25.1141 22.8542L25 20.8574ZM3 20.8574L2.8859 22.8542L2.9429 22.8574H3V20.8574ZM2.89746 20.8516L2.69472 22.8413L2.73896 22.8458L2.78336 22.8483L2.89746 20.8516ZM2.00488 19.959L0.00719023 20.055L0.00972354 20.1077L0.015031 20.1602L2.00488 19.959ZM2 19.8574H0V19.9055L0.00230742 19.9535L2 19.8574ZM2 3L0 3V3H2ZM2.89746 2.00488L2.80233 0.00714648L2.74849 0.00971043L2.69486 0.0151707L2.89746 2.00488ZM3 2V0H2.95241L2.90487 0.00226367L3 2ZM25 2L25.0951 0.00226367L25.0476 0H25V2ZM25.1025 2.00488L25.3051 0.0151708L25.2515 0.00971043L25.1977 0.00714648L25.1025 2.00488ZM4 18.8574V20.8574H24V18.8574V16.8574H4V18.8574ZM24 18.8574H26V4H24H22V18.8574H24ZM24 4V2H4V4V6H24V4ZM4 4H2V18.8574H4H6V4H4ZM26 19.8574L24.0023 19.7614L23.9974 19.8629L25.9951 19.959L27.9928 20.055L27.9977 19.9535L26 19.8574ZM25.9951 19.959L24.0053 19.7578C24.0534 19.2817 24.4276 18.91 24.8998 18.8619L25.1025 20.8516L25.3053 22.8413C26.7187 22.6972 27.8416 21.5777 27.985 20.1602L25.9951 19.959ZM25.1025 20.8516L24.9884 18.8548L24.8859 18.8607L25 20.8574L25.1141 22.8542L25.2166 22.8483L25.1025 20.8516ZM25 20.8574V18.8574H3V20.8574V22.8574H25V20.8574ZM3 20.8574L3.1141 18.8607L3.01156 18.8548L2.89746 20.8516L2.78336 22.8483L2.8859 22.8542L3 20.8574ZM2.89746 20.8516L3.1002 18.8619C3.57242 18.91 3.9466 19.2817 3.99473 19.7578L2.00488 19.959L0.015031 20.1602C0.158372 21.5777 1.28126 22.6972 2.69472 22.8413L2.89746 20.8516ZM2.00488 19.959L4.00258 19.8629L3.99769 19.7614L2 19.8574L0.00230742 19.9535L0.00719023 20.055L2.00488 19.959ZM2 19.8574H4V3H2H0V19.8574H2ZM2 3L4 3C4 3.51806 3.60718 3.94296 3.10006 3.99459L2.89746 2.00488L2.69486 0.0151707C1.17948 0.169471 2.02656e-06 1.44659 0 3L2 3ZM2.89746 2.00488L2.99259 4.00262L3.09513 3.99774L3 2L2.90487 0.00226367L2.80233 0.00714648L2.89746 2.00488ZM3 2V4H25V2V0H3V2ZM25 2L24.9049 3.99774L25.0074 4.00262L25.1025 2.00488L25.1977 0.00714648L25.0951 0.00226367L25 2ZM25.1025 2.00488L24.8999 3.99459C24.3928 3.94296 24 3.51806 24 3H26H28C28 1.44659 26.8205 0.169471 25.3051 0.0151708L25.1025 2.00488ZM26 3H24V19.8574H26H28V3H26Z" fill="black" mask="url(#path-1-outside-1_0_9)"/>
|
||||
</g>
|
||||
<path id="Polygon 341" d="M14.2946 7.14286L15.7793 9.71429H12.81L14.2946 7.14286Z" fill="#1C6A51"/>
|
||||
<path id="Polygon 342" d="M14.2946 15.7143L15.7793 13.1429H12.81L14.2946 15.7143Z" fill="#1C6A51"/>
|
||||
<path id="Polygon 343" d="M18.5804 11.4286L16.0089 12.9132L16.0089 9.94396L18.5804 11.4286Z" fill="#1C6A51"/>
|
||||
<path id="Polygon 344" d="M10.0089 11.4286L12.5804 12.9132L12.5804 9.94396L10.0089 11.4286Z" fill="#1C6A51"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,26 @@
|
||||
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319834">
|
||||
<g id="Group 2901">
|
||||
<g id="Union">
|
||||
<mask id="path-1-outside-1_0_2939" maskUnits="userSpaceOnUse" x="0" y="0" width="28" height="23" fill="black">
|
||||
<rect fill="white" width="28" height="23"/>
|
||||
<path d="M4 19H24V4H4V19ZM26 20C26 20.5523 25.5523 21 25 21H3C2.44772 21 2 20.5523 2 20V3C2 2.48232 2.39333 2.05621 2.89746 2.00488L3 2H25L25.1025 2.00488C25.6067 2.05621 26 2.48232 26 3V20Z"/>
|
||||
</mask>
|
||||
<path d="M4 19H24V4H4V19ZM26 20C26 20.5523 25.5523 21 25 21H3C2.44772 21 2 20.5523 2 20V3C2 2.48232 2.39333 2.05621 2.89746 2.00488L3 2H25L25.1025 2.00488C25.6067 2.05621 26 2.48232 26 3V20Z" fill="#1C6A51"/>
|
||||
<path d="M4 19H2V21H4V19ZM24 19V21H26V19H24ZM24 4H26V2H24V4ZM4 4V2H2V4H4ZM3 21L3 23H3V21ZM2.89746 2.00488L2.80233 0.00714648L2.74849 0.00971043L2.69486 0.0151707L2.89746 2.00488ZM3 2V0H2.95241L2.90487 0.00226367L3 2ZM25 2L25.0951 0.00226367L25.0476 0H25V2ZM25.1025 2.00488L25.3051 0.0151708L25.2515 0.00971043L25.1977 0.00714648L25.1025 2.00488ZM4 19V21H24V19V17H4V19ZM24 19H26V4H24H22V19H24ZM24 4V2H4V4V6H24V4ZM4 4H2V19H4H6V4H4ZM26 20H24C24 19.4477 24.4477 19 25 19V21V23C26.6569 23 28 21.6569 28 20H26ZM25 21V19H3V21V23H25V21ZM3 21L3 19C3.55228 19 4 19.4477 4 20H2H0C0 21.6569 1.34315 23 3 23L3 21ZM2 20H4V3H2H0V20H2ZM2 3H4C4 3.51806 3.60718 3.94296 3.10006 3.99459L2.89746 2.00488L2.69486 0.0151707C1.17948 0.16947 0 1.44659 0 3H2ZM2.89746 2.00488L2.99259 4.00262L3.09513 3.99774L3 2L2.90487 0.00226367L2.80233 0.00714648L2.89746 2.00488ZM3 2V4H25V2V0H3V2ZM25 2L24.9049 3.99774L25.0074 4.00262L25.1025 2.00488L25.1977 0.00714648L25.0951 0.00226367L25 2ZM25.1025 2.00488L24.8999 3.99459C24.3928 3.94296 24 3.51806 24 3H26H28C28 1.44659 26.8205 0.169471 25.3051 0.0151708L25.1025 2.00488ZM26 3H24V20H26H28V3H26Z" fill="black" mask="url(#path-1-outside-1_0_2939)"/>
|
||||
</g>
|
||||
<path id="Polygon 341" d="M14.2946 7.14286L15.7793 9.71429H12.81L14.2946 7.14286Z" fill="#1C6A51"/>
|
||||
<path id="Polygon 342" d="M14.2946 15.7143L15.7793 13.1429H12.81L14.2946 15.7143Z" fill="#1C6A51"/>
|
||||
<path id="Polygon 343" d="M18.5804 11.4286L16.0089 12.9132L16.0089 9.94396L18.5804 11.4286Z" fill="#1C6A51"/>
|
||||
<path id="Polygon 344" d="M10.0089 11.4286L12.5804 12.9132L12.5804 9.94396L10.0089 11.4286Z" fill="#1C6A51"/>
|
||||
</g>
|
||||
<g id="Union_2">
|
||||
<mask id="path-7-outside-2_0_2939" maskUnits="userSpaceOnUse" x="13.4163" y="10.5484" width="22.4181" height="22.6646" fill="black">
|
||||
<rect fill="white" x="13.4163" y="10.5484" width="22.4181" height="22.6646"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.6606 16.8666C20.5094 16.8011 20.3483 16.9363 20.3866 17.0966L22.6238 26.4621C22.6671 26.6435 22.9133 26.6721 22.997 26.5054L24.8275 22.8597L28.1564 26.8269C28.2274 26.9115 28.3536 26.9226 28.4382 26.8516L28.968 26.407C29.0526 26.336 29.0637 26.2099 28.9926 26.1253L25.7 22.2013L29.4733 21.0713C29.652 21.0178 29.6666 20.7704 29.4955 20.6962L20.6606 16.8666Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.6606 16.8666C20.5094 16.8011 20.3483 16.9363 20.3866 17.0966L22.6238 26.4621C22.6671 26.6435 22.9133 26.6721 22.997 26.5054L24.8275 22.8597L28.1564 26.8269C28.2274 26.9115 28.3536 26.9226 28.4382 26.8516L28.968 26.407C29.0526 26.336 29.0637 26.2099 28.9926 26.1253L25.7 22.2013L29.4733 21.0713C29.652 21.0178 29.6666 20.7704 29.4955 20.6962L20.6606 16.8666Z" fill="#1C6A51"/>
|
||||
<path d="M20.3866 17.0966L18.4413 17.5613L18.4413 17.5613L20.3866 17.0966ZM20.6606 16.8666L19.8652 18.7017L19.8652 18.7017L20.6606 16.8666ZM22.6238 26.4621L24.569 25.9974L24.569 25.9974L22.6238 26.4621ZM22.997 26.5054L21.2097 25.6079L21.2097 25.6079L22.997 26.5054ZM24.8275 22.8597L26.3596 21.5741L24.4048 19.2444L23.0402 21.9622L24.8275 22.8597ZM28.1564 26.8269L26.6244 28.1125L28.1564 26.8269ZM28.4382 26.8516L27.1526 25.3195L28.4382 26.8516ZM28.968 26.407L30.2536 27.9391L28.968 26.407ZM28.9926 26.1253L30.5247 24.8397L28.9926 26.1253ZM25.7 22.2013L25.1263 20.2853L22.2135 21.1576L24.1679 23.4869L25.7 22.2013ZM29.4733 21.0713L30.0471 22.9872L30.0471 22.9872L29.4733 21.0713ZM29.4955 20.6962L28.7001 22.5312L28.7001 22.5312L29.4955 20.6962ZM20.3866 17.0966L22.3318 16.6319C22.6765 18.0746 21.2261 19.2916 19.8652 18.7017L20.6606 16.8666L21.4561 15.0316C19.7927 14.3106 18.0201 15.798 18.4413 17.5613L20.3866 17.0966ZM22.6238 26.4621L24.569 25.9974L22.3318 16.6319L20.3866 17.0966L18.4413 17.5613L20.6785 26.9268L22.6238 26.4621ZM22.997 26.5054L21.2097 25.6079C21.9631 24.1075 24.1789 24.3643 24.569 25.9974L22.6238 26.4621L20.6785 26.9268C21.1553 28.9227 23.8636 29.2367 24.7844 27.4028L22.997 26.5054ZM24.8275 22.8597L23.0402 21.9622L21.2097 25.6079L22.997 26.5054L24.7844 27.4028L26.6149 23.7571L24.8275 22.8597ZM28.1564 26.8269L29.6885 25.5414L26.3596 21.5741L24.8275 22.8597L23.2954 24.1452L26.6244 28.1125L28.1564 26.8269ZM28.4382 26.8516L27.1526 25.3195C27.9142 24.6805 29.0495 24.7798 29.6885 25.5414L28.1564 26.8269L26.6244 28.1125C27.4054 29.0433 28.793 29.1647 29.7238 28.3837L28.4382 26.8516ZM28.968 26.407L27.6824 24.8749L27.1526 25.3195L28.4382 26.8516L29.7238 28.3837L30.2536 27.9391L28.968 26.407ZM28.9926 26.1253L27.4606 27.4108C26.8216 26.6493 26.9209 25.5139 27.6824 24.8749L28.968 26.407L30.2536 27.9391C31.1843 27.1581 31.3057 25.7705 30.5247 24.8397L28.9926 26.1253ZM25.7 22.2013L24.1679 23.4869L27.4606 27.4108L28.9926 26.1253L30.5247 24.8397L27.2321 20.9157L25.7 22.2013ZM29.4733 21.0713L28.8996 19.1554L25.1263 20.2853L25.7 22.2013L26.2738 24.1172L30.0471 22.9872L29.4733 21.0713ZM29.4955 20.6962L28.7001 22.5312C27.1597 21.8636 27.2913 19.637 28.8996 19.1554L29.4733 21.0713L30.0471 22.9872C32.0127 22.3986 32.1736 19.6772 30.2909 18.8612L29.4955 20.6962ZM20.6606 16.8666L19.8652 18.7017L28.7001 22.5312L29.4955 20.6962L30.2909 18.8612L21.4561 15.0316L20.6606 16.8666Z" fill="black" mask="url(#path-7-outside-2_0_2939)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,461 @@
|
||||
import { forwardRef, useEffect, useLayoutEffect, Fragment, type ReactNode, useId, useRef, useState } from "react"
|
||||
import { SWHeaderActions } from "./components/SWHeaderActions"
|
||||
import { SWHeaderColorMenu } from "./components/SWHeaderColorMenu"
|
||||
import { SWHeaderDocumentTitle } from "./components/SWHeaderDocumentTitle"
|
||||
import {
|
||||
SWHeaderColorControl, SWHeaderDimensionStyle, SWHeaderLayerControl,
|
||||
SWHeaderLineStyle, SWHeaderTextStyle,
|
||||
} from "./components/SWHeaderFields"
|
||||
import { SWHeaderHome } from "./components/SWHeaderHome"
|
||||
import { SWHeaderLayerMenu } from "./components/SWHeaderLayerMenu"
|
||||
import { SWHeaderLineStyleMenu } from "./components/SWHeaderLineStyleMenu"
|
||||
import { SWHeaderMore, type SWHeaderMoreItem } from "./components/SWHeaderMore"
|
||||
import { SWHeaderProperties } from "./components/SWHeaderProperties"
|
||||
import { SWHeaderPropertyBar } from "./components/SWHeaderPropertyBar"
|
||||
import { SWHeaderCommitPresence } from "./components/SWHeaderInput"
|
||||
import { SWHeaderKGuide } from "./components/SWHeaderKGuide"
|
||||
import printIcon from "../../assets/print.svg"
|
||||
import printHover from "../../assets/print-hover.svg"
|
||||
import { SWHeaderPropertyPanel } from "./components/SWHeaderPropertyPanel"
|
||||
import { SWHeaderDimensionStyleMenu, SWHeaderTextStyleMenu } from "./components/SWHeaderStyleListMenus"
|
||||
import { SWHeaderWindowControls } from "./components/SWHeaderWindowControls"
|
||||
import type { SWHeaderControlId, SWHeaderControlConfig, SWHeaderFrameControlId, SWHeaderHideableControlId, SWHeaderMenuId, SWHeaderProps } from "./types"
|
||||
|
||||
const defaults = {
|
||||
layer: { name: "0" },
|
||||
color: { value: "ByLayer", swatch: "#ffffff" },
|
||||
lineStyle: { name: "ByLayer", weight: "ByLayer", scale: "1.00" },
|
||||
textStyle: { font: "Arial", name: "Standard" },
|
||||
dimensionStyle: { name: "Standard" },
|
||||
}
|
||||
|
||||
/** `data-menu` of each field (its Figma variant name). */
|
||||
const menuSlot: Record<SWHeaderMenuId, string> = {
|
||||
layer: "layer", color: "color", lineStyle: "line-style", textStyle: "text-style", dimensionStyle: "dimension-style",
|
||||
}
|
||||
|
||||
const defaultMenuOrder = Object.keys(menuSlot) as SWHeaderMenuId[]
|
||||
const defaultControls: SWHeaderControlConfig[] = (["document", "drawingMode", ...defaultMenuOrder, "details", "propertyBar", "propertyPanel", "print", "popupGuide"] as const).map(id => ({ id }))
|
||||
const frameControls: readonly SWHeaderFrameControlId[] = ["home", "windowMove", "minimize", "close"]
|
||||
const isMenu = (id: string): id is SWHeaderMenuId => id in menuSlot
|
||||
|
||||
/**
|
||||
* Menu order: listed known ids first (duplicates and unknown ids ignored), missing ids after them in
|
||||
* the default order. Menus only swap places among the slots menus already take; other parts never move.
|
||||
*/
|
||||
function orderMenus(items: SWHeaderControlConfig[], menuOrder?: readonly SWHeaderMenuId[]) {
|
||||
if (!menuOrder) return items
|
||||
const listed = menuOrder.filter((id, index) => isMenu(id) && menuOrder.indexOf(id) === index)
|
||||
const rank = [...listed, ...defaultMenuOrder.filter(id => !listed.includes(id))]
|
||||
const menus = items.filter(item => isMenu(item.id)).sort((a, b) => rank.indexOf(a.id as SWHeaderMenuId) - rank.indexOf(b.id as SWHeaderMenuId))
|
||||
let next = 0
|
||||
return items.map(item => (isMenu(item.id) ? menus[next++] : item))
|
||||
}
|
||||
|
||||
/**
|
||||
* SW Header (Figma node 26:8944, 1920 x 49). Pure presentation: values are props,
|
||||
* user actions are callbacks, window behavior belongs to the host program.
|
||||
*/
|
||||
export const SWHeader = forwardRef<HTMLElement, SWHeaderProps>(function SWHeader(
|
||||
{
|
||||
documentTitle,
|
||||
controls,
|
||||
hiddenControls,
|
||||
menuOrder,
|
||||
documents,
|
||||
documentMenuOpen,
|
||||
onDocumentMenuOpenChange,
|
||||
onDocumentSelect,
|
||||
activeDocumentId,
|
||||
onDocumentClose,
|
||||
onDocumentNew,
|
||||
onDocumentOpen,
|
||||
documentIcon,
|
||||
documentNewLabel,
|
||||
documentOpenLabel,
|
||||
drawingModeLabel = "그리기모드",
|
||||
layer = defaults.layer,
|
||||
layers,
|
||||
activeLayerId,
|
||||
layerMenuOpen,
|
||||
onLayerMenuOpenChange,
|
||||
onLayerSelect,
|
||||
onLayerVisibilityChange,
|
||||
onLayerFreezeChange,
|
||||
onLayerLockChange,
|
||||
onLayerDetail,
|
||||
color = defaults.color,
|
||||
lineStyle = defaults.lineStyle,
|
||||
textStyle = defaults.textStyle,
|
||||
dimensionStyle = defaults.dimensionStyle,
|
||||
colors,
|
||||
userColors,
|
||||
colorMode,
|
||||
colorMenuOpen,
|
||||
onColorMenuOpenChange,
|
||||
onColorSelect,
|
||||
onColorModeChange,
|
||||
onColorInput,
|
||||
onColorPick,
|
||||
onColorDetail,
|
||||
lineTypes,
|
||||
lineWeights,
|
||||
activeLineTypeId,
|
||||
activeLineWeightId,
|
||||
lineStyleMenuOpen,
|
||||
onLineStyleMenuOpenChange,
|
||||
onLineTypeSelect,
|
||||
onLineWeightSelect,
|
||||
onLineWeightValueChange,
|
||||
onLineScaleChange,
|
||||
onLineStyleDetail,
|
||||
textStyles,
|
||||
activeTextStyleId,
|
||||
textStyleMenuOpen,
|
||||
onTextStyleMenuOpenChange,
|
||||
onTextStyleSelect,
|
||||
onTextStyleDetail,
|
||||
dimensionStyles,
|
||||
activeDimensionStyleId,
|
||||
dimensionStyleMenuOpen,
|
||||
onDimensionStyleMenuOpenChange,
|
||||
onDimensionStyleSelect,
|
||||
onDimensionStyleDetail,
|
||||
activeMenu = null,
|
||||
propertyBarOpen,
|
||||
defaultPropertyBarOpen = false,
|
||||
propertyBar,
|
||||
onPropertyBarOpenChange,
|
||||
propertyPanelOpen,
|
||||
propertyPanel,
|
||||
onPropertyPanelOpenChange,
|
||||
disabled = false,
|
||||
tools,
|
||||
logo,
|
||||
logoSrc,
|
||||
logoAlt,
|
||||
onHomeClick,
|
||||
onDocumentClick,
|
||||
onLayerOpen,
|
||||
onColorOpen,
|
||||
onLineStyleOpen,
|
||||
onTextStyleOpen,
|
||||
onDimensionStyleOpen,
|
||||
onPropertyBarToggle,
|
||||
onPropertyPanelToggle,
|
||||
onPrint,
|
||||
onPopupGuide,
|
||||
onWindowMoveStart,
|
||||
onMinimize,
|
||||
onClose,
|
||||
className,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
// The logo is never in this list: it is always shown. Hidden parts are unmounted (no space, no popup, no callback).
|
||||
const removed = new Set<string>(hiddenControls ?? [])
|
||||
const shown = (id: SWHeaderHideableControlId) => !removed.has(id)
|
||||
const seen = new Set<SWHeaderControlId>()
|
||||
const configuration = orderMenus((controls ?? defaultControls).filter(item => item.visible !== false && shown(item.id) && !seen.has(item.id) && !!seen.add(item.id)), menuOrder)
|
||||
const enabled = (id: SWHeaderControlId) => configuration.some(item => item.id === id)
|
||||
// Default design with removed object parts: same structure and breakpoints, but the script decides what
|
||||
// moves into More (the CSS alone assumes every field is present). Untouched defaults stay CSS-only.
|
||||
const scripted = controls !== undefined || [...removed].some(id => !frameControls.includes(id as SWHeaderFrameControlId))
|
||||
const presentRef = useRef(new Set(configuration.map(item => item.id)))
|
||||
presentRef.current = new Set(configuration.map(item => item.id))
|
||||
const label = (id: SWHeaderControlId, fallback: string) => configuration.find(item => item.id === id)?.label ?? fallback
|
||||
const [internalBarOpen, setInternalBarOpen] = useState(defaultPropertyBarOpen)
|
||||
const barOpen = !disabled && enabled("propertyBar") && (propertyBarOpen ?? internalBarOpen)
|
||||
const toggleBar = () => {
|
||||
if (!enabled("propertyBar") || disabled) return
|
||||
const next = !barOpen
|
||||
if (propertyBarOpen === undefined) setInternalBarOpen(next)
|
||||
onPropertyBarToggle?.()
|
||||
onPropertyBarOpenChange?.(next)
|
||||
}
|
||||
// Uncontrolled open dropdown (one at a time). A controlled `*MenuOpen` prop takes precedence.
|
||||
const [openMenu, setOpenMenu] = useState<SWHeaderMenuId | null>(null)
|
||||
// Representative slot (UI state only, never program data): a field chosen in More takes the layer
|
||||
// field's place while it is in More at the current width (CSS decides, see sw.css "Representative slot").
|
||||
const [promoted, setPromoted] = useState<SWHeaderMenuId | null>(null)
|
||||
const focusRef = useRef<SWHeaderMenuId | null>(null)
|
||||
const fieldsRef = useRef<HTMLDivElement>(null)
|
||||
const panelAnchorRef = useRef<HTMLDivElement>(null)
|
||||
const panelId = useId()
|
||||
const barId = useId()
|
||||
const [internalPanelOpen, setInternalPanelOpen] = useState(false)
|
||||
const panelOpen = !disabled && enabled("propertyPanel") && (propertyPanelOpen ?? internalPanelOpen)
|
||||
const setPanelOpen = (next: boolean) => {
|
||||
if (propertyPanelOpen === undefined) setInternalPanelOpen(next)
|
||||
if (next !== panelOpen) onPropertyPanelOpenChange?.(next)
|
||||
if (next) setOpenMenu(null)
|
||||
}
|
||||
const togglePanel = () => { onPropertyPanelToggle?.(); setPanelOpen(!panelOpen) }
|
||||
const closePanel = () => {
|
||||
if (propertyPanelOpen !== undefined && !onPropertyPanelOpenChange) onPropertyPanelToggle?.()
|
||||
setPanelOpen(false)
|
||||
const header = panelAnchorRef.current?.closest(".hm-sw-header")
|
||||
const trigger = header?.querySelector<HTMLButtonElement>('[data-property-panel-trigger]')
|
||||
const more = header?.querySelector<HTMLButtonElement>('.hm-sw-header__more-trigger')
|
||||
;(trigger?.getClientRects().length ? trigger : more)?.focus()
|
||||
}
|
||||
|
||||
|
||||
const [headerWidth, setHeaderWidth] = useState(1920)
|
||||
const [barOffset, setBarOffset] = useState(0)
|
||||
useLayoutEffect(() => {
|
||||
const header = fieldsRef.current?.closest(".hm-sw-header")
|
||||
if (!header) return
|
||||
const update = () => {
|
||||
const bounds = header.getBoundingClientRect()
|
||||
setHeaderWidth(bounds.width)
|
||||
setBarOffset((panelAnchorRef.current?.getBoundingClientRect().left ?? bounds.left) - bounds.left)
|
||||
}
|
||||
const observer = new ResizeObserver(update)
|
||||
update()
|
||||
observer.observe(header)
|
||||
if (panelAnchorRef.current) observer.observe(panelAnchorRef.current)
|
||||
return () => observer.disconnect()
|
||||
}, [])
|
||||
const menuIds = configuration.map(item => item.id).filter((id): id is SWHeaderMenuId => id in menuSlot)
|
||||
const baseMenu = menuIds.includes("layer") ? "layer" : menuIds[0]
|
||||
const validPromoted = promoted && enabled(promoted) ? promoted : null
|
||||
const isOverflow = (id: SWHeaderControlId) => id === "color" ? headerWidth < 900 : ["lineStyle", "textStyle", "dimensionStyle"].includes(id) ? headerWidth < 1800 : ["propertyBar", "propertyPanel", "details"].includes(id) ? headerWidth < 1080 : ["print", "popupGuide"].includes(id) ? headerWidth < 640 : false
|
||||
const representative = validPromoted && isOverflow(validPromoted) ? validPromoted : baseMenu
|
||||
const fieldHidden = (id: SWHeaderMenuId) => id !== representative && (isOverflow(id) || (id === baseMenu && representative !== baseMenu))
|
||||
// Removal is structural: close stale internal dropdown state silently, never send data callbacks.
|
||||
const controlKey = configuration.map(item => item.id).join(",")
|
||||
useEffect(() => {
|
||||
if (openMenu && !enabled(openMenu)) setOpenMenu(null)
|
||||
if (promoted && !enabled(promoted)) setPromoted(null)
|
||||
if (!enabled("propertyPanel")) setInternalPanelOpen(false)
|
||||
if (!enabled("propertyBar")) setInternalBarOpen(false)
|
||||
}, [controlKey, openMenu, promoted])
|
||||
|
||||
const hasMenu: Record<SWHeaderMenuId, boolean> = {
|
||||
layer: layers !== undefined,
|
||||
color: colors !== undefined,
|
||||
lineStyle: lineTypes !== undefined || lineWeights !== undefined,
|
||||
textStyle: textStyles !== undefined,
|
||||
dimensionStyle: dimensionStyles !== undefined,
|
||||
}
|
||||
const openChange = (id: SWHeaderMenuId, callback?: (open: boolean) => void) => (next: boolean) => {
|
||||
if (next && panelOpen) setPanelOpen(false)
|
||||
setOpenMenu(current => (next ? id : current === id ? null : current))
|
||||
callback?.(next)
|
||||
}
|
||||
const menuOpenProps = (id: SWHeaderMenuId, controlled: boolean | undefined, callback?: (open: boolean) => void) => ({
|
||||
open: enabled(id) && (controlled ?? openMenu === id),
|
||||
onOpenChange: openChange(id, callback),
|
||||
})
|
||||
const layerOpen = menuOpenProps("layer", layerMenuOpen, onLayerMenuOpenChange)
|
||||
const colorOpen = menuOpenProps("color", colorMenuOpen, onColorMenuOpenChange)
|
||||
const lineStyleOpen = menuOpenProps("lineStyle", lineStyleMenuOpen, onLineStyleMenuOpenChange)
|
||||
const textStyleOpen = menuOpenProps("textStyle", textStyleMenuOpen, onTextStyleMenuOpenChange)
|
||||
const dimensionStyleOpen = menuOpenProps("dimensionStyle", dimensionStyleMenuOpen, onDimensionStyleMenuOpenChange)
|
||||
const openers: Record<SWHeaderMenuId, { onOpenChange: (open: boolean) => void; onOpen?: () => void }> = {
|
||||
layer: { onOpenChange: layerOpen.onOpenChange, onOpen: onLayerOpen },
|
||||
color: { onOpenChange: colorOpen.onOpenChange, onOpen: onColorOpen },
|
||||
lineStyle: { onOpenChange: lineStyleOpen.onOpenChange, onOpen: onLineStyleOpen },
|
||||
textStyle: { onOpenChange: textStyleOpen.onOpenChange, onOpen: onTextStyleOpen },
|
||||
dimensionStyle: { onOpenChange: dimensionStyleOpen.onOpenChange, onOpen: onDimensionStyleOpen },
|
||||
}
|
||||
|
||||
/**
|
||||
* More → field: show the field in the representative slot (choosing the layer gives the slot back),
|
||||
* open its dropdown right below and move focus to the field. Calls the field's open callback once,
|
||||
* exactly like a click on the field; no data callback is called.
|
||||
*/
|
||||
const promote = (id: SWHeaderMenuId) => () => {
|
||||
setPromoted(id === baseMenu ? null : id)
|
||||
focusRef.current = id
|
||||
if (hasMenu[id]) openers[id].onOpenChange(true)
|
||||
openers[id].onOpen?.()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = focusRef.current
|
||||
if (!id) return
|
||||
focusRef.current = null
|
||||
// The slot is the field itself, or the wrapper of a field with a dropdown (layer: .hm-sw-header__layer).
|
||||
const slot = fieldsRef.current?.querySelector<HTMLElement>(scripted ? `[data-control="${id}"]` : id === "layer" ? ":scope > :is(.hm-sw-header__layer, [data-menu='layer'])" : `:scope > [data-menu="${menuSlot[id]}"]`)
|
||||
const field = slot?.matches(".hm-sw-header__field") ? slot : slot?.querySelector<HTMLElement>(".hm-sw-header__field")
|
||||
field?.focus()
|
||||
})
|
||||
|
||||
const currentLayerName = layers?.find(item => item.id === activeLayerId)?.name ?? layer.name
|
||||
const allMoreItems: SWHeaderMoreItem[] = [
|
||||
{ id: "layer", menu: "layer", label: "레이어", value: currentLayerName, tier: "slot", selected: activeMenu === "layer", disabled, onSelect: promote("layer") },
|
||||
{ id: "color", menu: "color", label: "색상", value: color.value, tier: "narrow", selected: activeMenu === "color", disabled, onSelect: promote("color") },
|
||||
{ id: "lineStyle", menu: "line-style", label: "선스타일", value: lineStyle.name, tier: "aux", selected: activeMenu === "lineStyle", disabled, onSelect: promote("lineStyle") },
|
||||
{ id: "textStyle", menu: "text-style", label: "문자스타일", value: textStyle.name, tier: "aux", selected: activeMenu === "textStyle", disabled, onSelect: promote("textStyle") },
|
||||
{ id: "dimensionStyle", menu: "dimension-style", label: "치수스타일", value: dimensionStyle.name, tier: "aux", selected: activeMenu === "dimensionStyle", disabled, onSelect: promote("dimensionStyle") },
|
||||
{ id: "propertyBar", label: "속성Bar", tier: "panel", checked: barOpen, disabled, onSelect: toggleBar },
|
||||
{ id: "propertyPanel", label: "속성창", tier: "panel", checked: panelOpen, disabled, onSelect: togglePanel },
|
||||
{ id: "print", label: "인쇄", tier: "action", disabled, onSelect: onPrint },
|
||||
{ id: "popupGuide", label: "팝업 가이드", tier: "action", disabled, guide: true, onSelect: onPopupGuide },
|
||||
]
|
||||
|
||||
const moreItems = configuration.flatMap(config => {
|
||||
const item = allMoreItems.find(item => item.id === config.id)
|
||||
if (!item) return []
|
||||
const hidden = config.id in menuSlot ? fieldHidden(config.id as SWHeaderMenuId) : isOverflow(config.id)
|
||||
return [{ ...item, label: config.label ?? item.label, visible: scripted ? hidden : undefined }]
|
||||
})
|
||||
const fieldNodes: Record<SWHeaderMenuId, ReactNode> = {
|
||||
layer: (layers ? (
|
||||
<SWHeaderLayerMenu label={label("layer", "레이어")}
|
||||
value={layer}
|
||||
layers={layers}
|
||||
activeLayerId={activeLayerId}
|
||||
{...layerOpen}
|
||||
onClick={onLayerOpen}
|
||||
selected={activeMenu === "layer"}
|
||||
disabled={disabled}
|
||||
onLayerSelect={onLayerSelect}
|
||||
onVisibilityChange={onLayerVisibilityChange}
|
||||
onFreezeChange={onLayerFreezeChange}
|
||||
onLockChange={onLayerLockChange}
|
||||
onDetail={onLayerDetail}
|
||||
/>
|
||||
) : (
|
||||
<SWHeaderLayerControl label={label("layer", "레이어")} value={layer} selected={activeMenu === "layer"} disabled={disabled} onClick={onLayerOpen} />
|
||||
)),
|
||||
color: (colors ? (
|
||||
<SWHeaderColorMenu label={label("color", "색상")}
|
||||
value={color}
|
||||
colors={colors}
|
||||
userColors={userColors}
|
||||
mode={colorMode}
|
||||
{...colorOpen}
|
||||
onClick={onColorOpen}
|
||||
selected={activeMenu === "color"}
|
||||
disabled={disabled}
|
||||
onColorSelect={onColorSelect}
|
||||
onModeChange={onColorModeChange}
|
||||
onColorInput={onColorInput}
|
||||
onPick={onColorPick}
|
||||
onDetail={onColorDetail}
|
||||
/>
|
||||
) : (
|
||||
<SWHeaderColorControl label={label("color", "색상")} value={color} selected={activeMenu === "color"} disabled={disabled} onClick={onColorOpen} />
|
||||
)),
|
||||
lineStyle: (hasMenu.lineStyle ? (
|
||||
<SWHeaderLineStyleMenu label={label("lineStyle", "선스타일")}
|
||||
value={lineStyle}
|
||||
lineTypes={lineTypes}
|
||||
lineWeights={lineWeights}
|
||||
activeLineTypeId={activeLineTypeId}
|
||||
activeLineWeightId={activeLineWeightId}
|
||||
{...lineStyleOpen}
|
||||
onClick={onLineStyleOpen}
|
||||
selected={activeMenu === "lineStyle"}
|
||||
disabled={disabled}
|
||||
onLineTypeSelect={onLineTypeSelect}
|
||||
onLineWeightSelect={onLineWeightSelect}
|
||||
onLineWeightValueChange={onLineWeightValueChange}
|
||||
onScaleChange={onLineScaleChange}
|
||||
onDetail={onLineStyleDetail}
|
||||
/>
|
||||
) : (
|
||||
<SWHeaderLineStyle label={label("lineStyle", "선스타일")} value={lineStyle} selected={activeMenu === "lineStyle"} disabled={disabled} onClick={onLineStyleOpen} />
|
||||
)),
|
||||
textStyle: (textStyles ? (
|
||||
<SWHeaderTextStyleMenu label={label("textStyle", "문자스타일")}
|
||||
value={textStyle}
|
||||
textStyles={textStyles}
|
||||
activeTextStyleId={activeTextStyleId}
|
||||
{...textStyleOpen}
|
||||
onClick={onTextStyleOpen}
|
||||
selected={activeMenu === "textStyle"}
|
||||
disabled={disabled}
|
||||
onTextStyleSelect={onTextStyleSelect}
|
||||
onDetail={onTextStyleDetail}
|
||||
/>
|
||||
) : (
|
||||
<SWHeaderTextStyle label={label("textStyle", "문자스타일")} value={textStyle} selected={activeMenu === "textStyle"} disabled={disabled} onClick={onTextStyleOpen} />
|
||||
)),
|
||||
dimensionStyle: (dimensionStyles ? (
|
||||
<SWHeaderDimensionStyleMenu label={label("dimensionStyle", "치수스타일")}
|
||||
value={dimensionStyle}
|
||||
dimensionStyles={dimensionStyles}
|
||||
activeDimensionStyleId={activeDimensionStyleId}
|
||||
{...dimensionStyleOpen}
|
||||
onClick={onDimensionStyleOpen}
|
||||
selected={activeMenu === "dimensionStyle"}
|
||||
disabled={disabled}
|
||||
onDimensionStyleSelect={onDimensionStyleSelect}
|
||||
onDetail={onDimensionStyleDetail}
|
||||
/>
|
||||
) : (
|
||||
<SWHeaderDimensionStyle label={label("dimensionStyle", "치수스타일")} value={dimensionStyle} selected={activeMenu === "dimensionStyle"} disabled={disabled} onClick={onDimensionStyleOpen} />
|
||||
)),
|
||||
}
|
||||
const documentNode = (<SWHeaderDocumentTitle
|
||||
label={label("document", "문서")}
|
||||
title={documentTitle}
|
||||
onClick={onDocumentClick}
|
||||
documents={documents}
|
||||
menuOpen={documentMenuOpen}
|
||||
onMenuOpenChange={onDocumentMenuOpenChange}
|
||||
onDocumentSelect={onDocumentSelect}
|
||||
activeDocumentId={activeDocumentId}
|
||||
onDocumentClose={onDocumentClose}
|
||||
onNew={onDocumentNew}
|
||||
onOpen={onDocumentOpen}
|
||||
documentIcon={documentIcon}
|
||||
newLabel={documentNewLabel}
|
||||
openLabel={documentOpenLabel}
|
||||
/>)
|
||||
const configuredNodes: Partial<Record<SWHeaderControlId, ReactNode>> = {
|
||||
...fieldNodes,
|
||||
document: documentNode,
|
||||
drawingMode: <span className="hm-sw-header__mode">{label("drawingMode", drawingModeLabel)}</span>,
|
||||
details: <span className="hm-sw-header__properties-caption">{label("details", "상세보기")}</span>,
|
||||
propertyBar: <button type="button" className="hm-sw-header__toggle" aria-pressed={barOpen} aria-controls={barOpen ? barId : undefined} disabled={disabled} onClick={toggleBar}>{label("propertyBar", "속성Bar")}</button>,
|
||||
propertyPanel: <button type="button" className="hm-sw-header__toggle" data-property-panel-trigger="" aria-pressed={panelOpen} aria-haspopup={propertyPanel ? "dialog" : undefined} aria-expanded={panelOpen} aria-controls={panelOpen ? panelId : undefined} disabled={disabled} onClick={togglePanel}>{label("propertyPanel", "속성창")}</button>,
|
||||
print: <button type="button" className="hm-sw-header__action hm-sw-header__action--print" aria-label={label("print", "인쇄")} disabled={disabled} onClick={onPrint}><img className="hm-sw-header__icon" src={printIcon} alt="" /><img className="hm-sw-header__icon-hover" src={printHover} alt="" /></button>,
|
||||
popupGuide: <SWHeaderKGuide label={label("popupGuide", "팝업 가이드")} disabled={disabled} onClick={onPopupGuide} />,
|
||||
}
|
||||
const presence = (id: SWHeaderControlId, node: ReactNode) => <SWHeaderCommitPresence.Provider value={() => presentRef.current.has(id)}>{node}</SWHeaderCommitPresence.Provider>
|
||||
const actionsShown = controls === undefined && (enabled("print") || enabled("popupGuide"))
|
||||
const windowShown = actionsShown || frameControls.slice(1).some(shown)
|
||||
const windowReduced = removed.size > 0 && (frameControls.some(id => removed.has(id)) || (controls === undefined && !(enabled("print") && enabled("popupGuide"))))
|
||||
// CSS-only default layout: a field holding the first slot instead of the removed layer is styled as promoted.
|
||||
const promotedSlot = validPromoted ?? (!scripted && baseMenu && baseMenu !== "layer" ? baseMenu : null)
|
||||
const barData = propertyBar ?? (propertyPanel && {
|
||||
selection: propertyPanel.selection,
|
||||
properties: propertyPanel.properties,
|
||||
onPropertyChange: propertyPanel.onPropertyChange,
|
||||
})
|
||||
|
||||
return (
|
||||
<header ref={ref} className={`hm-sw-header${className ? ` ${className}` : ""}`} {...rest}>
|
||||
<div className="hm-sw-header__bar">
|
||||
<SWHeaderHome onClick={shown("home") ? onHomeClick : undefined} interactive={shown("home")} logo={logo} logoSrc={logoSrc} logoAlt={logoAlt} />
|
||||
{controls === undefined && enabled("document") && documentNode}
|
||||
<div ref={panelAnchorRef} className="hm-sw-header__object-area" role="toolbar" aria-label="객체 수정" data-configured={controls !== undefined || undefined} data-promoted={promotedSlot ? menuSlot[promotedSlot] : undefined}>
|
||||
{controls === undefined && enabled("drawingMode") && <span className="hm-sw-header__mode">{drawingModeLabel}</span>}
|
||||
<div ref={fieldsRef} className="hm-sw-header__fields" data-configured={controls !== undefined || undefined}>
|
||||
{controls ? configuration.map(config => {
|
||||
const hidden = config.id in menuSlot ? fieldHidden(config.id as SWHeaderMenuId) : isOverflow(config.id) || (config.id === "drawingMode" && headerWidth < 1180)
|
||||
return <div key={config.id} className="hm-sw-header__control" data-control={config.id} data-hidden={hidden || undefined}>{presence(config.id, configuredNodes[config.id])}</div>
|
||||
}) : scripted ? menuIds.map(id => (
|
||||
<div key={id} className="hm-sw-header__control" data-control={id} data-hidden={fieldHidden(id) || undefined} data-representative={(id === representative && id !== baseMenu) || undefined}>{presence(id, fieldNodes[id])}</div>
|
||||
)) : menuIds.map(id => <Fragment key={id}>{fieldNodes[id]}</Fragment>)}
|
||||
{tools}
|
||||
</div>
|
||||
{moreItems.some(item => item.visible !== false) && <SWHeaderMore items={moreItems} />}
|
||||
{controls === undefined && <SWHeaderProperties barOpen={barOpen} panelOpen={panelOpen} panelId={propertyPanel ? panelId : undefined} disabled={disabled} onBarToggle={toggleBar} onPanelToggle={togglePanel} showCaption={enabled("details")} showBar={enabled("propertyBar")} showPanel={enabled("propertyPanel")} />}
|
||||
</div>
|
||||
{windowShown && <div className="hm-sw-header__window" data-configured={controls !== undefined || undefined} data-reduced={windowReduced || undefined}>
|
||||
{actionsShown && <SWHeaderActions disabled={disabled} onPrint={onPrint} onPopupGuide={onPopupGuide} showPrint={enabled("print")} showPopupGuide={enabled("popupGuide")} />}
|
||||
<SWHeaderWindowControls onWindowMoveStart={onWindowMoveStart} onMinimize={onMinimize} onClose={onClose} showWindowMove={shown("windowMove")} showMinimize={shown("minimize")} showClose={shown("close")} />
|
||||
</div>}
|
||||
</div>
|
||||
{barOpen && barData && <div className="hm-sw-header__bar-anchor" style={{ left: barOffset, width: `calc(100% - ${barOffset}px)` }}><SWHeaderPropertyBar {...barData} disabled={disabled || propertyBar?.disabled} id={barId} /></div>}
|
||||
{panelOpen && propertyPanel && <SWHeaderPropertyPanel {...propertyPanel} id={panelId} anchorRef={panelAnchorRef} onClose={closePanel} />}
|
||||
</header>
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,26 @@
|
||||
import printIcon from "../../../assets/print.svg"
|
||||
import printIconHover from "../../../assets/print-hover.svg"
|
||||
import { SWHeaderKGuide } from "./SWHeaderKGuide"
|
||||
|
||||
export interface SWHeaderActionsProps {
|
||||
disabled?: boolean
|
||||
onPrint?: () => void
|
||||
onPopupGuide?: () => void
|
||||
/** Individual buttons (default true). The pill shrinks to the remaining button; none renders nothing. */
|
||||
showPrint?: boolean
|
||||
showPopupGuide?: boolean
|
||||
}
|
||||
|
||||
/** Print and popup-guide buttons inside the dark rounded pill. */
|
||||
export function SWHeaderActions({ disabled, onPrint, onPopupGuide, showPrint = true, showPopupGuide = true }: SWHeaderActionsProps) {
|
||||
if (!showPrint && !showPopupGuide) return null
|
||||
return (
|
||||
<div className="hm-sw-header__actions" role="group" aria-label="바로가기" data-partial={showPrint && showPopupGuide ? undefined : ""}>
|
||||
{showPrint && <button type="button" className="hm-sw-header__action hm-sw-header__action--print" aria-label="인쇄" disabled={disabled} onClick={onPrint}>
|
||||
<img className="hm-sw-header__icon" src={printIcon} alt="" aria-hidden="true" />
|
||||
<img className="hm-sw-header__icon-hover" src={printIconHover} alt="" aria-hidden="true" />
|
||||
</button>}
|
||||
{showPopupGuide && <SWHeaderKGuide disabled={disabled} onClick={onPopupGuide} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import colorPickIcon from "../../../assets/colorize-default.svg"
|
||||
import colorPickHover from "../../../assets/colorize-hover.svg"
|
||||
import type { SWHeaderColorInputValue, SWHeaderColorMode, SWHeaderColorOption, SWHeaderColorValue } from "../types"
|
||||
import { SWHeaderColorChip, SWHeaderMenuDetail, useSWHeaderDropdown } from "./SWHeaderDropdown"
|
||||
import { SWHeaderColorControl } from "./SWHeaderFields"
|
||||
import { SWHeaderInput, parseSWHeaderColor } from "./SWHeaderInput"
|
||||
|
||||
export interface SWHeaderColorMenuProps {
|
||||
/** Current color shown in the field, the input and its chip. */
|
||||
label?: string
|
||||
value: SWHeaderColorValue
|
||||
/** Basic palette (Figma: white, red, green, yellow, blue, magenta, cyan). Host data. */
|
||||
colors: SWHeaderColorOption[]
|
||||
/** User colors: extra rows below the palette, any count. */
|
||||
userColors?: SWHeaderColorOption[]
|
||||
/** Color mode; null still means explicit color. The required mode indicator remembers its last mode. */
|
||||
mode?: SWHeaderColorMode | null
|
||||
/** Controlled open state. Leave undefined to let the component manage it. */
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
/** Called on every click of the field (also when the dropdown toggles). */
|
||||
onClick?: () => void
|
||||
/** Keeps the field in its selected (#FFC600) look while closed. */
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onColorSelect?: (option: SWHeaderColorOption) => void
|
||||
onModeChange?: (mode: SWHeaderColorMode) => void
|
||||
onColorInput?: (value: SWHeaderColorInputValue) => void
|
||||
/** Color pick (색상 추출). The dropdown closes so the host can pick from the drawing. */
|
||||
onPick?: () => void
|
||||
/** "색상 상세". Optional host action; omitting it keeps hover and performs no CAD action. */
|
||||
onDetail?: () => void
|
||||
}
|
||||
|
||||
const modes: { mode: SWHeaderColorMode; label: string }[] = [
|
||||
{ mode: "byLayer", label: "ByLayer" },
|
||||
{ mode: "byBlock", label: "ByBlock" },
|
||||
]
|
||||
|
||||
/** Value text of an option: its `value`, else the RGB text of its color. */
|
||||
const optionValue = (option: SWHeaderColorOption) => option.value ?? parseSWHeaderColor(option.color)?.value ?? option.color
|
||||
|
||||
/**
|
||||
* Color field with its dropdown (Figma header/menu/색상 26:10986, user colors 26:11036): ByLayer /
|
||||
* ByBlock (26:12666 / 26:12659), 22px palette, user color rows, color pick + RGB input, "색상 상세".
|
||||
* Every control only reports through its callback; the host updates `value` / `mode`. Choosing a
|
||||
* color or a mode keeps the dropdown open (several properties can be set in a row).
|
||||
* Latest request restores Figma hover for the mode pair and detail pill; chips/pick remain unchanged.
|
||||
*/
|
||||
export function SWHeaderColorMenu({
|
||||
value, label, colors, userColors, mode, open: openProp, onOpenChange, onClick, selected = false, disabled = false,
|
||||
onColorSelect, onModeChange, onColorInput, onPick, onDetail,
|
||||
}: SWHeaderColorMenuProps) {
|
||||
const { open, setOpen, close, panelRef, triggerRef, menuId, panelStyle, shifted, rootProps } = useSWHeaderDropdown({ open: openProp, onOpenChange, disabled })
|
||||
const swatch = value.swatch ?? "#ffe500"
|
||||
const current = parseSWHeaderColor(value.value) ?? parseSWHeaderColor(swatch)
|
||||
const inputValue = current?.value ?? value.value
|
||||
const [lastMode, setLastMode] = useState<SWHeaderColorMode>(mode ?? "byLayer")
|
||||
useEffect(() => { if (mode) setLastMode(mode) }, [mode])
|
||||
const selectedMode = mode ?? lastMode
|
||||
|
||||
const chip = (option: SWHeaderColorOption) => {
|
||||
const pressed = !mode && current !== null && parseSWHeaderColor(option.color)?.hex === current.hex
|
||||
const label = option.label ?? optionValue(option)
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
className="hm-sw-header__color-option"
|
||||
title={label}
|
||||
aria-label={label}
|
||||
aria-pressed={pressed}
|
||||
disabled={option.disabled}
|
||||
onClick={() => onColorSelect?.(option)}
|
||||
>
|
||||
<SWHeaderColorChip color={option.color} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...rootProps} className="hm-sw-header__menu hm-sw-header__menu--color" data-menu="color">
|
||||
<SWHeaderColorControl
|
||||
buttonRef={triggerRef}
|
||||
label={label}
|
||||
value={value}
|
||||
selected={selected || open}
|
||||
expanded={open}
|
||||
controls={open ? menuId : undefined}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
onClick?.()
|
||||
setOpen(!open)
|
||||
}}
|
||||
/>
|
||||
{open && (
|
||||
<div
|
||||
ref={panelRef}
|
||||
id={menuId}
|
||||
className="hm-sw-header__menu-panel hm-sw-header__color-menu"
|
||||
role="group"
|
||||
aria-label="색상"
|
||||
data-shifted={shifted ? "true" : undefined}
|
||||
style={panelStyle}
|
||||
>
|
||||
<div className="hm-sw-header__color-modes" role="group" aria-label="색상 기준">
|
||||
{modes.map(item => (
|
||||
<button
|
||||
key={item.mode}
|
||||
type="button"
|
||||
className={`hm-sw-header__color-mode hm-sw-header__color-mode--${item.mode === "byLayer" ? "layer" : "block"}`}
|
||||
aria-pressed={selectedMode === item.mode}
|
||||
onClick={() => {
|
||||
if (selectedMode === item.mode) return
|
||||
setLastMode(item.mode)
|
||||
onModeChange?.(item.mode)
|
||||
}}
|
||||
>
|
||||
<span className="hm-sw-header__menu-text">{item.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="hm-sw-header__color-palette">
|
||||
{colors.length > 0 && <div className="hm-sw-header__color-row" role="group" aria-label="기본 색상">{colors.map(chip)}</div>}
|
||||
{userColors && userColors.length > 0 && (
|
||||
<div className="hm-sw-header__color-row hm-sw-header__color-row--user" role="group" aria-label="사용자 색상">{userColors.map(chip)}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="hm-sw-header__color-tools">
|
||||
<button
|
||||
type="button"
|
||||
className="hm-sw-header__color-pick"
|
||||
aria-label="색상 추출"
|
||||
title="색상 추출"
|
||||
disabled={!onPick}
|
||||
onClick={() => {
|
||||
onPick?.()
|
||||
close(true)
|
||||
}}
|
||||
>
|
||||
<img className="hm-sw-header__colorize-default" src={colorPickIcon} alt="" />
|
||||
<img className="hm-sw-header__colorize-hover" src={colorPickHover} alt="" />
|
||||
</button>
|
||||
<span className="hm-sw-header__color-input">
|
||||
<SWHeaderColorChip color={swatch} />
|
||||
<SWHeaderInput
|
||||
aria-label="색상값 (R,G,B)"
|
||||
inputMode="text"
|
||||
value={inputValue}
|
||||
parse={draft => parseSWHeaderColor(draft)?.value ?? null}
|
||||
onCommit={onColorInput ? next => {
|
||||
const parsed = parseSWHeaderColor(next)
|
||||
if (parsed) onColorInput(parsed)
|
||||
} : undefined}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className="hm-sw-header__menu-footer">
|
||||
<SWHeaderMenuDetail label="색상 상세" onClick={onDetail && (() => { onDetail(); close(true) })} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode } from "react"
|
||||
import documentIcon from "../../../assets/document-icon.svg"
|
||||
import type { SWHeaderDocument } from "../types"
|
||||
|
||||
export interface SWHeaderDocumentTitleProps {
|
||||
title: string
|
||||
label?: string
|
||||
/** Called on every click of the file name (also when the document menu toggles). */
|
||||
onClick?: () => void
|
||||
/**
|
||||
* Documents shown in the dropdown below the file name. When omitted, the file name is a plain
|
||||
* button without a dropdown (the 0.1.0 behavior). An empty array opens a menu with only New / Open.
|
||||
*/
|
||||
documents?: SWHeaderDocument[]
|
||||
/** Controlled open state of the document menu. Leave undefined to let the component manage it. */
|
||||
menuOpen?: boolean
|
||||
onMenuOpenChange?: (open: boolean) => void
|
||||
onDocumentSelect?: (document: SWHeaderDocument) => void
|
||||
/**
|
||||
* Id of the current document. Its name is shown in #FFC600; the list keeps the order of
|
||||
* `documents` (selecting another document never moves rows).
|
||||
*/
|
||||
activeDocumentId?: string
|
||||
/**
|
||||
* Close Document. When given, every document row gets an X button ("문서 닫기"), hidden until
|
||||
* the row is hovered or focused (space is always reserved). The host closes the document and
|
||||
* updates `documents` / `activeDocumentId`; the header never picks the next document itself.
|
||||
*/
|
||||
onDocumentClose?: (document: SWHeaderDocument) => void
|
||||
onNew?: () => void
|
||||
onOpen?: () => void
|
||||
/** Icon used for documents without their own `icon`. Defaults to the Figma document icon. */
|
||||
documentIcon?: ReactNode
|
||||
newLabel?: string
|
||||
openLabel?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* File name button. With `documents`, it is a disclosure button that opens the document menu
|
||||
* (Figma header/document_tit 26:10662): click / Enter / Space toggle it, ArrowDown opens it and
|
||||
* focuses the first entry, Escape or a click outside closes it.
|
||||
*/
|
||||
export function SWHeaderDocumentTitle({
|
||||
title,
|
||||
label = "문서",
|
||||
onClick,
|
||||
documents,
|
||||
menuOpen,
|
||||
onMenuOpenChange,
|
||||
onDocumentSelect,
|
||||
activeDocumentId,
|
||||
onDocumentClose,
|
||||
onNew,
|
||||
onOpen,
|
||||
documentIcon: iconOverride,
|
||||
newLabel = "+ New",
|
||||
openLabel = "Open",
|
||||
}: SWHeaderDocumentTitleProps) {
|
||||
const hasMenu = documents !== undefined
|
||||
const [internalOpen, setInternalOpen] = useState(false)
|
||||
const open = hasMenu && (menuOpen ?? internalOpen)
|
||||
const rootRef = useRef<HTMLDivElement>(null)
|
||||
const triggerRef = useRef<HTMLButtonElement>(null)
|
||||
const menuId = useId()
|
||||
// Set when the keyboard opens the menu or a document is closed, so the first entry gets focus
|
||||
// once the menu (re)renders and focus is never lost with a removed row.
|
||||
const focusFirstRef = useRef(false)
|
||||
const [, rerender] = useState(0)
|
||||
|
||||
const setOpen = (next: boolean) => {
|
||||
if (menuOpen === undefined) setInternalOpen(next)
|
||||
onMenuOpenChange?.(next)
|
||||
}
|
||||
const close = (restoreFocus: boolean) => {
|
||||
setOpen(false)
|
||||
if (restoreFocus) triggerRef.current?.focus()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!rootRef.current?.contains(event.target as Node)) setOpen(false)
|
||||
}
|
||||
document.addEventListener("pointerdown", onPointerDown)
|
||||
return () => document.removeEventListener("pointerdown", onPointerDown)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (open && focusFirstRef.current) {
|
||||
focusFirstRef.current = false
|
||||
entries()[0]?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
const entries = () => Array.from(rootRef.current?.querySelectorAll<HTMLButtonElement>(".hm-sw-header__document-menu button:not(:disabled)") ?? [])
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (!hasMenu) return
|
||||
if (event.key === "Escape" && open) {
|
||||
event.stopPropagation()
|
||||
close(true)
|
||||
return
|
||||
}
|
||||
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return
|
||||
event.preventDefault()
|
||||
if (!open) {
|
||||
focusFirstRef.current = true
|
||||
setOpen(true)
|
||||
return
|
||||
}
|
||||
const list = entries()
|
||||
const index = list.indexOf(document.activeElement as HTMLButtonElement)
|
||||
const next = event.key === "ArrowDown" ? (index + 1) % list.length : (index - 1 + list.length) % list.length
|
||||
list[next]?.focus()
|
||||
}
|
||||
|
||||
const items = documents ?? []
|
||||
const active = items.find(item => item.id === activeDocumentId)
|
||||
|
||||
const iconFor = (item: SWHeaderDocument) =>
|
||||
item.icon ?? iconOverride ?? <img className="hm-sw-header__document-icon-default" src={documentIcon} alt="" />
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="hm-sw-header__document"
|
||||
onKeyDown={onKeyDown}
|
||||
onBlur={event => {
|
||||
// Tabbing out of the file name and its menu closes the menu; focus is never trapped.
|
||||
if (open && !rootRef.current?.contains(event.relatedTarget as Node | null)) setOpen(false)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
className="hm-sw-header__document-button"
|
||||
title={title}
|
||||
aria-label={`${label}: ${title}`}
|
||||
aria-expanded={hasMenu ? open : undefined}
|
||||
aria-controls={open ? menuId : undefined}
|
||||
data-selected={open ? "true" : undefined}
|
||||
onClick={() => {
|
||||
onClick?.()
|
||||
if (hasMenu) setOpen(!open)
|
||||
}}
|
||||
>
|
||||
<span className="hm-sw-header__document-name">{title}</span>
|
||||
<span className="hm-sw-header__arrow" aria-hidden="true" />
|
||||
</button>
|
||||
{open && (
|
||||
<div id={menuId} className="hm-sw-header__document-menu" role="group" aria-label="문서 목록">
|
||||
{items.length > 0 && (
|
||||
<ul className="hm-sw-header__document-list">
|
||||
{items.map(item => (
|
||||
<SWHeaderDocumentItem
|
||||
key={item.id}
|
||||
document={item}
|
||||
icon={iconFor(item)}
|
||||
active={item === active}
|
||||
onSelect={() => {
|
||||
onDocumentSelect?.(item)
|
||||
close(true)
|
||||
}}
|
||||
onClose={onDocumentClose ? () => {
|
||||
// Park focus inside the header before the row may disappear, then refocus the
|
||||
// first entry after the host updates the list. The menu stays open.
|
||||
triggerRef.current?.focus()
|
||||
focusFirstRef.current = true
|
||||
onDocumentClose(item)
|
||||
rerender(count => count + 1)
|
||||
} : undefined}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<div className="hm-sw-header__document-footer">
|
||||
<button type="button" className="hm-sw-header__document-action" onClick={() => { onNew?.(); close(true) }}>{newLabel}</button>
|
||||
<button type="button" className="hm-sw-header__document-action" onClick={() => { onOpen?.(); close(true) }}>{openLabel}</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* One document row (32px): select button (icon 29 x 12 + name) and an optional Close Document
|
||||
* button for this document (shown on row hover / focus). Sibling buttons, so closing never selects.
|
||||
*/
|
||||
function SWHeaderDocumentItem({ document, icon, active, onSelect, onClose }: {
|
||||
document: SWHeaderDocument
|
||||
icon: ReactNode
|
||||
active: boolean
|
||||
onSelect: () => void
|
||||
onClose?: () => void
|
||||
}) {
|
||||
return (
|
||||
<li className="hm-sw-header__document-row" data-active={active ? "true" : undefined} data-disabled={document.disabled ? "true" : undefined}>
|
||||
<button
|
||||
type="button"
|
||||
className="hm-sw-header__document-item"
|
||||
title={document.title ?? document.name}
|
||||
aria-current={active ? "true" : undefined}
|
||||
disabled={document.disabled}
|
||||
onClick={onSelect}
|
||||
>
|
||||
<span className="hm-sw-header__document-icon" aria-hidden="true">{icon}</span>
|
||||
<span className="hm-sw-header__document-item-name">{document.name}</span>
|
||||
</button>
|
||||
{onClose && (
|
||||
<button type="button" className="hm-sw-header__document-close" aria-label="문서 닫기" title={`${document.name} 닫기`} onClick={onClose}>
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" aria-hidden="true" focusable="false">
|
||||
<path d="M1 1l6 6M7 1L1 7" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { useEffect, useId, useLayoutEffect, useRef, useState, type FocusEvent, type KeyboardEvent } from "react"
|
||||
import detailIcon from "../../../assets/layer-detail.svg"
|
||||
import type { ButtonHTMLAttributes, ReactNode } from "react"
|
||||
|
||||
export interface SWHeaderDropdownOptions {
|
||||
/** Controlled open state. Leave undefined to let the component manage it. */
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
disabled?: boolean
|
||||
/** `start`: left edge under the field (color, text, dimension). `center`: centered under it (line style). */
|
||||
align?: "start" | "center"
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared behavior of the field dropdowns added after the layer dropdown (color, line style, text style,
|
||||
* dimension style), the same contract as SWHeaderLayerMenu: click / Enter / Space toggle (hover never
|
||||
* opens), ArrowDown opens and focuses the current (or first) entry, ArrowUp / ArrowDown move, Escape /
|
||||
* a click outside / Tab out close. The list overlays below the field and is shifted horizontally only
|
||||
* when it would leave the header (or the screen).
|
||||
*/
|
||||
export function useSWHeaderDropdown({ open: openProp, onOpenChange, disabled = false, align = "start" }: SWHeaderDropdownOptions) {
|
||||
const [internalOpen, setInternalOpen] = useState(false)
|
||||
const open = !disabled && (openProp ?? internalOpen)
|
||||
const rootRef = useRef<HTMLDivElement>(null)
|
||||
const panelRef = useRef<HTMLDivElement>(null)
|
||||
const triggerRef = useRef<HTMLButtonElement>(null)
|
||||
const menuId = useId()
|
||||
const focusEntryRef = useRef(false)
|
||||
// Left offset of the panel from the field; `base` is the unshifted position for `align`.
|
||||
const [position, setPosition] = useState({ left: 0, base: 0 })
|
||||
|
||||
const setOpen = (next: boolean) => {
|
||||
if (openProp === undefined) setInternalOpen(next)
|
||||
onOpenChange?.(next)
|
||||
}
|
||||
const close = (restoreFocus: boolean) => {
|
||||
setOpen(false)
|
||||
if (restoreFocus) triggerRef.current?.focus()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!rootRef.current?.contains(event.target as Node)) setOpen(false)
|
||||
}
|
||||
document.addEventListener("pointerdown", onPointerDown)
|
||||
return () => document.removeEventListener("pointerdown", onPointerDown)
|
||||
})
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!open) return
|
||||
const update = () => {
|
||||
const root = rootRef.current
|
||||
const panel = panelRef.current
|
||||
if (!root || !panel) return
|
||||
const bounds = (root.closest(".hm-sw-header") ?? document.documentElement).getBoundingClientRect()
|
||||
// 1px inside the bounds so the outline ring (drawn 1px outside the panel) never overflows.
|
||||
const min = Math.max(bounds.left, 0) + 1
|
||||
const max = Math.min(bounds.right, document.documentElement.clientWidth) - 1
|
||||
const field = root.getBoundingClientRect()
|
||||
const base = align === "center" ? Math.round((field.width - panel.offsetWidth) / 2) : 0
|
||||
let left = field.left + base
|
||||
if (left + panel.offsetWidth > max) left = max - panel.offsetWidth
|
||||
if (left < min) left = min
|
||||
const next = Math.round(left - field.left)
|
||||
setPosition(current => (current.left === next && current.base === base ? current : { left: next, base }))
|
||||
}
|
||||
update()
|
||||
window.addEventListener("resize", update)
|
||||
return () => window.removeEventListener("resize", update)
|
||||
}) // Reposition after a host-driven control reorder as well as open/resize.
|
||||
|
||||
/** Focusable entries of the panel in DOM order (rows, inputs, footer). */
|
||||
const entries = () => Array.from(panelRef.current?.querySelectorAll<HTMLElement>("button:not(:disabled), input:not(:disabled)") ?? [])
|
||||
|
||||
useEffect(() => {
|
||||
if (open && focusEntryRef.current) {
|
||||
focusEntryRef.current = false
|
||||
const current = panelRef.current?.querySelector<HTMLElement>('[aria-current="true"]:not(:disabled), [aria-pressed="true"]:not(:disabled)')
|
||||
;(current ?? entries()[0])?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === "Escape" && open) {
|
||||
event.stopPropagation()
|
||||
close(true)
|
||||
return
|
||||
}
|
||||
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return
|
||||
if (!open) {
|
||||
if (event.target !== triggerRef.current) return
|
||||
event.preventDefault()
|
||||
focusEntryRef.current = true
|
||||
setOpen(true)
|
||||
return
|
||||
}
|
||||
const list = entries()
|
||||
if (list.length === 0) return
|
||||
event.preventDefault()
|
||||
const index = list.indexOf(document.activeElement as HTMLElement)
|
||||
if (index < 0) {
|
||||
list[event.key === "ArrowDown" ? 0 : list.length - 1]?.focus()
|
||||
return
|
||||
}
|
||||
list[(index + (event.key === "ArrowDown" ? 1 : -1) + list.length) % list.length]?.focus()
|
||||
}
|
||||
|
||||
const onBlur = (event: FocusEvent<HTMLDivElement>) => {
|
||||
// Tabbing out of the field and its dropdown closes it; focus is never trapped.
|
||||
if (open && !rootRef.current?.contains(event.relatedTarget as Node | null)) setOpen(false)
|
||||
}
|
||||
|
||||
return {
|
||||
open,
|
||||
setOpen,
|
||||
close,
|
||||
rootRef,
|
||||
panelRef,
|
||||
triggerRef,
|
||||
menuId,
|
||||
/** Panel `left` relative to the field (centered and / or shifted to stay on screen). */
|
||||
panelStyle: position.left !== 0 ? { left: position.left } : undefined,
|
||||
/** The panel no longer sits under the field (the outline then rings the panel only). */
|
||||
shifted: position.left !== position.base,
|
||||
rootProps: { ref: rootRef, onKeyDown, onBlur, "data-open": open ? "true" : undefined },
|
||||
}
|
||||
}
|
||||
|
||||
export interface SWHeaderMenuDetailProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
||||
label: string
|
||||
/** Optional replacement icon; decorative, with the shared 14px size and state opacity. */
|
||||
icon?: ReactNode
|
||||
}
|
||||
|
||||
/** All five footer buttons use the text-style detail design (26:10789 / 26:10792).
|
||||
* Callback absence is a safe no-op; only an explicit disabled prop disables the button.
|
||||
* The actual CAD detail popup remains the host's responsibility.
|
||||
*/
|
||||
export function SWHeaderMenuDetail({ label, icon, className, disabled = false, ...props }: SWHeaderMenuDetailProps) {
|
||||
return (
|
||||
<button {...props} type="button" className={["hm-sw-header__menu-detail", className].filter(Boolean).join(" ")} data-detail={label} disabled={disabled}>
|
||||
{icon === undefined ? <img src={detailIcon} alt="" /> : <span className="hm-sw-header__detail-icon" aria-hidden="true">{icon}</span>}
|
||||
<span data-detail-label="">{label}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
/** 22px color chip (Figma color chip 22px 26:11091): black 20px tile, 18px swatch with a #C9C9C9 border. */
|
||||
export function SWHeaderColorChip({ color }: { color: string }) {
|
||||
return (
|
||||
<span className="hm-sw-header__color-chip" aria-hidden="true">
|
||||
<span style={{ background: color }} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { ReactNode, Ref } from "react"
|
||||
|
||||
export interface SWHeaderFieldProps {
|
||||
/** Accessible name of the field button, e.g. `레이어`. */
|
||||
label: string
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onClick?: () => void
|
||||
/** Modifier for the Figma width. */
|
||||
variant: "layer" | "color" | "line-style" | "text-style" | "dimension-style"
|
||||
/** `aux` fields move into the More menu below the compact breakpoint. */
|
||||
tier?: "main" | "aux"
|
||||
/** Open state of a dropdown owned by the field itself (layer, color, line / text / dimension style). Defaults to `selected`. */
|
||||
expanded?: boolean
|
||||
/** Id of that dropdown while it is open. */
|
||||
controls?: string
|
||||
buttonRef?: Ref<HTMLButtonElement>
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
/** Shared SW property field surface (user-confirmed gradient, border and shadow). */
|
||||
export function SWHeaderField({ label, selected = false, disabled, onClick, variant, tier = "main", expanded, controls, buttonRef, children }: SWHeaderFieldProps) {
|
||||
return (
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
className={`hm-sw-header__field hm-sw-header__field--${variant}`}
|
||||
data-menu={variant}
|
||||
data-tier={tier}
|
||||
data-selected={selected ? "true" : undefined}
|
||||
data-expanded={expanded ? "true" : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={expanded ?? selected}
|
||||
aria-controls={controls}
|
||||
aria-label={label}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
/** Small label (12px) above a bold value (14px) used by every field. */
|
||||
export function SWHeaderFieldText({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<span className="hm-sw-header__field-text">
|
||||
<span className="hm-sw-header__field-label">{label}</span>
|
||||
<span className="hm-sw-header__field-value" title={value}>{value}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import dimensionIcon from "../../../assets/menu-dimension-ico.svg"
|
||||
import dimensionIconHover from "../../../assets/menu-dimension-ico-hover.svg"
|
||||
import type {
|
||||
SWHeaderColorValue, SWHeaderDimensionStyleValue, SWHeaderLayer, SWHeaderLayerValue,
|
||||
SWHeaderLineStyleValue, SWHeaderTextStyleValue,
|
||||
} from "../types"
|
||||
import { SWHeaderField, SWHeaderFieldText, type SWHeaderFieldProps } from "./SWHeaderField"
|
||||
import { SWHeaderLayerIconArt, layerIconOn } from "./SWHeaderLayerIcons"
|
||||
|
||||
interface FieldBase extends Pick<SWHeaderFieldProps, "expanded" | "controls" | "buttonRef"> {
|
||||
label?: string
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export interface SWHeaderLayerControlProps extends FieldBase {
|
||||
value: SWHeaderLayerValue
|
||||
/** State of the current layer for the three field icons (gray when off). Defaults to all colored. */
|
||||
state?: Pick<SWHeaderLayer, "visible" | "frozen" | "locked">
|
||||
}
|
||||
|
||||
export function SWHeaderLayerControl({ value, state, label = "레이어", ...rest }: SWHeaderLayerControlProps) {
|
||||
return (
|
||||
<SWHeaderField label={label} variant="layer" {...rest}>
|
||||
<span className="hm-sw-header__layer-icons" aria-hidden="true">
|
||||
<SWHeaderLayerIconArt kind="visibility" on={layerIconOn("visibility", state)} />
|
||||
<SWHeaderLayerIconArt kind="freeze" on={layerIconOn("freeze", state)} />
|
||||
<SWHeaderLayerIconArt kind="lock" on={layerIconOn("lock", state)} />
|
||||
</span>
|
||||
<SWHeaderFieldText label={label} value={value.name} />
|
||||
</SWHeaderField>
|
||||
)
|
||||
}
|
||||
|
||||
export function SWHeaderColorControl({ value, label, ...rest }: FieldBase & { value: SWHeaderColorValue }) {
|
||||
return (
|
||||
<SWHeaderField label={label ?? "색상"} variant="color" {...rest}>
|
||||
<span className="hm-sw-header__chip" aria-hidden="true">
|
||||
<span style={{ background: value.swatch ?? "#ffe500" }} />
|
||||
</span>
|
||||
<SWHeaderFieldText label={label ?? "색상"} value={value.value} />
|
||||
</SWHeaderField>
|
||||
)
|
||||
}
|
||||
|
||||
export function SWHeaderLineStyle({ value, label, ...rest }: FieldBase & { value: SWHeaderLineStyleValue }) {
|
||||
return (
|
||||
<SWHeaderField label={label ?? "선스타일"} variant="line-style" tier="aux" {...rest}>
|
||||
<span className="hm-sw-header__linetype" aria-hidden="true"><i /><i /><i /><i /><i /></span>
|
||||
<span className="hm-sw-header__line-values">
|
||||
<SWHeaderFieldText label={label ?? "선스타일"} value={value.name} />
|
||||
<SWHeaderFieldText label="가중치" value={value.weight} />
|
||||
<SWHeaderFieldText label="스케일" value={value.scale} />
|
||||
</span>
|
||||
</SWHeaderField>
|
||||
)
|
||||
}
|
||||
|
||||
export function SWHeaderTextStyle({ value, label, ...rest }: FieldBase & { value: SWHeaderTextStyleValue }) {
|
||||
return (
|
||||
<SWHeaderField label={label ?? "문자스타일"} variant="text-style" tier="aux" {...rest}>
|
||||
<span className="hm-sw-header__font-name" title={value.font}>{value.font}</span>
|
||||
<SWHeaderFieldText label={label ?? "문자스타일"} value={value.name} />
|
||||
</SWHeaderField>
|
||||
)
|
||||
}
|
||||
|
||||
export function SWHeaderDimensionStyle({ value, label, ...rest }: FieldBase & { value: SWHeaderDimensionStyleValue }) {
|
||||
return (
|
||||
<SWHeaderField label={label ?? "치수스타일"} variant="dimension-style" tier="aux" {...rest}>
|
||||
<span className="hm-sw-header__dimension-icon" aria-hidden="true">
|
||||
<img className="hm-sw-header__dimension-icon-default" src={dimensionIcon} alt="" />
|
||||
<img className="hm-sw-header__dimension-icon-hover" src={dimensionIconHover} alt="" />
|
||||
</span>
|
||||
<SWHeaderFieldText label={label ?? "치수스타일"} value={value.name} />
|
||||
</SWHeaderField>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { ReactNode } from "react"
|
||||
import homeBackground from "../../../assets/home-bg.svg"
|
||||
import homeBackgroundHover from "../../../assets/home-bg-hover.svg"
|
||||
|
||||
export interface SWHeaderHomeProps {
|
||||
onClick?: () => void
|
||||
/** Logo image URL, fitted (contain) into the 32 x 17 logo slot. */
|
||||
logoSrc?: string
|
||||
/** Logo element (inline SVG, <img>, ...). Takes precedence over `logoSrc`. */
|
||||
logo?: ReactNode
|
||||
/** Accessible product name. The home button is announced as "<logoAlt> 홈". */
|
||||
logoAlt?: string
|
||||
/**
|
||||
* false: the logo stays in the same slot as a static image (no home button, hover or callback).
|
||||
* SWHeader always shows the logo; only the home action can be removed. Default true.
|
||||
*/
|
||||
interactive?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Home button. The slanted background and the logo slot position are the common SW design;
|
||||
* the logo itself belongs to each SW program, so no logo is rendered unless one is passed.
|
||||
*/
|
||||
export function SWHeaderHome({ onClick, logoSrc, logo, logoAlt, interactive = true }: SWHeaderHomeProps) {
|
||||
const content = logo ?? (logoSrc ? <img src={logoSrc} alt="" /> : null)
|
||||
if (!interactive) return (
|
||||
<div className="hm-sw-header__home" data-static="" role={logoAlt ? "img" : undefined} aria-label={logoAlt}>
|
||||
<img className="hm-sw-header__home-bg" src={homeBackground} alt="" aria-hidden="true" />
|
||||
{content && <span className="hm-sw-header__logo" aria-hidden="true">{content}</span>}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<button type="button" className="hm-sw-header__home" aria-label={logoAlt ? `${logoAlt} 홈` : "홈"} onClick={onClick}>
|
||||
<img className="hm-sw-header__home-bg" src={homeBackground} alt="" aria-hidden="true" />
|
||||
<img className="hm-sw-header__home-bg-hover" src={homeBackgroundHover} alt="" aria-hidden="true" />
|
||||
{content && <span className="hm-sw-header__logo" aria-hidden="true">{content}</span>}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
import { createContext, useContext, forwardRef, useEffect, useRef, useState, type InputHTMLAttributes } from "react"
|
||||
|
||||
/** Structural removal discards drafts; ordinary dropdown close retains the legacy commit contract. */
|
||||
export const SWHeaderCommitPresence = createContext<() => boolean>(() => true)
|
||||
|
||||
/** Built-in validation of the draft. `parse` overrides it (any custom format). */
|
||||
export type SWHeaderInputValueType = "text" | "number"
|
||||
|
||||
export interface SWHeaderInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type" | "size"> {
|
||||
/** Current (committed) value from the host. A new value replaces the draft unless it is being edited. */
|
||||
value: string
|
||||
/** Every edit of the draft (temporary value, not committed). */
|
||||
onValueChange?: (draft: string) => void
|
||||
/**
|
||||
* Committed value: Enter, leaving the input, or the dropdown around it closing. Called only when `parse`
|
||||
* accepts the draft and the result differs from `value`; an invalid draft is discarded and `value` restored.
|
||||
*/
|
||||
onCommit?: (value: string) => void
|
||||
/** Escape with an edited draft: the draft is restored to `value` (the event does not close the dropdown). */
|
||||
onCancel?: () => void
|
||||
/** `text` (default): trimmed, non-empty text. `number`: a decimal number (see `parseSWHeaderNumber`). */
|
||||
valueType?: SWHeaderInputValueType
|
||||
/** Validation + normalization, overriding `valueType`. Return the value to commit, or `null` for an invalid draft. */
|
||||
parse?: (draft: string) => string | null
|
||||
/** Default true (legacy dropdown behavior). False discards a draft when a dynamic property is removed. */
|
||||
commitOnUnmount?: boolean
|
||||
}
|
||||
|
||||
const parsers: Record<SWHeaderInputValueType, (draft: string) => string | null> = {
|
||||
text: draft => draft.trim() || null,
|
||||
number: draft => parseSWHeaderNumber(draft),
|
||||
}
|
||||
|
||||
/**
|
||||
* Editable SW input (Figma input 26:11656): red 14px value with the black text outline. Default has no
|
||||
* surface; Hover shows a 1px rgba(212,203,189,.25) border (24px); editing (focus) shows #0F0D0A with a
|
||||
* .3 border and the native caret in #E60403 (Figma cursor 26:11664, 1px; it blinks natively). The width
|
||||
* follows the text. A real <input>: selection, IME (Enter while composing never commits) and the host
|
||||
* value stay intact; values are never hard-coded here.
|
||||
*/
|
||||
export const SWHeaderInput = forwardRef<HTMLInputElement, SWHeaderInputProps>(function SWHeaderInput(
|
||||
{ value, onValueChange, onCommit, onCancel, valueType = "text", parse = parsers[valueType], commitOnUnmount = true, readOnly, disabled, className, style, onBlur, onKeyDown, ...rest },
|
||||
ref,
|
||||
) {
|
||||
const [draft, setDraft] = useState(value)
|
||||
const [invalid, setInvalid] = useState(false)
|
||||
const draftRef = useRef(value)
|
||||
const isPresent = useContext(SWHeaderCommitPresence)
|
||||
const pendingRef = useRef<string | null>(null)
|
||||
const latest = useRef({ value, parse, onCommit, commitOnUnmount, readOnly, disabled })
|
||||
latest.current = { value, parse, onCommit, commitOnUnmount, readOnly, disabled }
|
||||
|
||||
useEffect(() => {
|
||||
draftRef.current = value
|
||||
setDraft(value)
|
||||
setInvalid(false)
|
||||
pendingRef.current = null
|
||||
}, [value, readOnly, disabled])
|
||||
|
||||
const update = (next: string) => {
|
||||
draftRef.current = next
|
||||
setDraft(next)
|
||||
}
|
||||
|
||||
const commit = () => {
|
||||
const { value: current, parse: parseDraft, onCommit: report } = latest.current
|
||||
if (latest.current.readOnly || latest.current.disabled) return
|
||||
if (draftRef.current === current) return
|
||||
const next = parseDraft(draftRef.current)
|
||||
setInvalid(false)
|
||||
if (next === null) {
|
||||
update(current)
|
||||
return
|
||||
}
|
||||
update(next)
|
||||
if (next !== current && next !== pendingRef.current) { pendingRef.current = next; report?.(next) }
|
||||
}
|
||||
|
||||
// A click outside closes a dropdown before its input blurs: still commit what was typed.
|
||||
useEffect(() => () => {
|
||||
if (!isPresent() || !latest.current.commitOnUnmount || latest.current.readOnly || latest.current.disabled) return
|
||||
const { value: current, parse: parseDraft, onCommit: report } = latest.current
|
||||
if (draftRef.current === current) return
|
||||
const next = parseDraft(draftRef.current)
|
||||
if (next !== null && next !== current && next !== pendingRef.current) report?.(next)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
inputMode={valueType === "number" ? "decimal" : undefined}
|
||||
{...rest}
|
||||
className={`hm-sw-header__input${className ? ` ${className}` : ""}`}
|
||||
// Width follows the draft (digits are 1ch); the paddings are added in CSS.
|
||||
style={{ ["--hm-sw-input-chars" as string]: Math.max(draft.length, 1), ...style }}
|
||||
aria-invalid={invalid || undefined}
|
||||
data-invalid={invalid ? "true" : undefined}
|
||||
value={draft}
|
||||
// Without onCommit the value is shown read-only (same look, nothing is reported).
|
||||
readOnly={readOnly || !onCommit}
|
||||
disabled={disabled}
|
||||
onChange={event => {
|
||||
update(event.target.value)
|
||||
setInvalid(latest.current.parse(event.target.value) === null)
|
||||
onValueChange?.(event.target.value)
|
||||
}}
|
||||
onBlur={event => {
|
||||
commit()
|
||||
onBlur?.(event)
|
||||
}}
|
||||
onKeyDown={event => {
|
||||
onKeyDown?.(event)
|
||||
// IME composition (e.g. Hangul) owns Enter / Escape until it ends.
|
||||
if (event.defaultPrevented || event.nativeEvent.isComposing || event.keyCode === 229) return
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
commit()
|
||||
} else if (event.key === "Escape" && draftRef.current !== latest.current.value) {
|
||||
// The first Escape only cancels the edit; the next one closes the dropdown around it.
|
||||
event.stopPropagation()
|
||||
update(latest.current.value)
|
||||
setInvalid(false)
|
||||
onCancel?.()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
/** `r,g,b` (0-255, spaces allowed) or `#rgb` / `#rrggbb` → normalized color, otherwise null. */
|
||||
export function parseSWHeaderColor(text: string): { value: string; rgb: [number, number, number]; hex: string } | null {
|
||||
const trimmed = text.trim()
|
||||
let rgb: number[] | null = null
|
||||
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed)
|
||||
if (hex) {
|
||||
const digits = hex[1].length === 3 ? hex[1].split("").map(digit => digit + digit).join("") : hex[1]
|
||||
rgb = [0, 2, 4].map(index => parseInt(digits.slice(index, index + 2), 16))
|
||||
} else {
|
||||
const parts = trimmed.split(/\s*,\s*/)
|
||||
if (parts.length === 3 && parts.every(part => /^\d{1,3}$/.test(part))) rgb = parts.map(Number)
|
||||
}
|
||||
if (!rgb || rgb.some(channel => channel > 255)) return null
|
||||
const [r, g, b] = rgb
|
||||
return { value: `${r},${g},${b}`, rgb: [r, g, b], hex: `#${rgb.map(channel => channel.toString(16).padStart(2, "0")).join("")}` }
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimal number parser (`-1.5`, `0.25`, `.5`) → its text, otherwise null. `min` / `max` are inclusive;
|
||||
* `minExclusive` makes `min` exclusive (e.g. a scale greater than zero).
|
||||
*/
|
||||
export function parseSWHeaderNumber(
|
||||
text: string,
|
||||
{ min = -Infinity, max = Infinity, minExclusive = false }: { min?: number; max?: number; minExclusive?: boolean } = {},
|
||||
): string | null {
|
||||
const trimmed = text.trim()
|
||||
if (!/^-?(\d+\.?\d*|\.\d+)$/.test(trimmed)) return null
|
||||
const number = Number(trimmed)
|
||||
if (!Number.isFinite(number) || number > max || (minExclusive ? number <= min : number < min)) return null
|
||||
return trimmed.endsWith(".") ? trimmed.slice(0, -1) : trimmed
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { useEffect, useId, useRef, useState, type RefObject } from "react"
|
||||
import popupGuide from "../../../assets/popup-guide.svg"
|
||||
import popupGuideHover from "../../../assets/popup-guide-hover.svg"
|
||||
import guideImage from "../../../assets/k-guide.png"
|
||||
import defaultMask from "../../../assets/k-guide-mask-default.svg"
|
||||
import panMask from "../../../assets/k-guide-mask-pan.svg"
|
||||
import fitMask from "../../../assets/k-guide-mask-fit.svg"
|
||||
import { useSWHeaderOverlay } from "./SWHeaderPropertyPanel"
|
||||
|
||||
/** Original K button. Its button and floating guide form one hover/focus region. */
|
||||
export function SWHeaderKGuide({ disabled, onClick, label = "팝업 가이드" }: { disabled?: boolean; onClick?: () => void; label?: string }) {
|
||||
const triggerRef = useRef<HTMLButtonElement>(null)
|
||||
const regionRef = useRef<HTMLDivElement>(null)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [dismissed, setDismissed] = useState(false)
|
||||
const guideId = useId()
|
||||
const visible = open && !disabled && !dismissed
|
||||
return (
|
||||
<div ref={regionRef} className="hm-sw-k-guide" data-open={visible || undefined} onPointerEnter={() => { setDismissed(false); setOpen(true) }} onPointerLeave={() => { setOpen(false); setDismissed(false) }} onFocus={() => setOpen(true)} onBlur={event => {
|
||||
if (!regionRef.current?.contains(event.relatedTarget as Node | null)) { setOpen(false); setDismissed(false) }
|
||||
}} onKeyDown={event => { if (event.key === "Escape" && visible) { event.stopPropagation(); setDismissed(true) } }}>
|
||||
<button ref={triggerRef} type="button" className="hm-sw-header__action hm-sw-header__action--guide" aria-label={label} aria-describedby={visible ? guideId : undefined} disabled={disabled} onClick={onClick}>
|
||||
<img className="hm-sw-header__icon" src={popupGuide} alt="" aria-hidden="true" />
|
||||
<img className="hm-sw-header__icon-hover" src={popupGuideHover} alt="" aria-hidden="true" />
|
||||
</button>
|
||||
{visible && <SWHeaderKGuidePreview anchorRef={triggerRef} id={guideId} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Same preview for the existing More entry on narrow headers; no duplicate K button. */
|
||||
export function SWHeaderKGuidePreview({ anchorRef, id }: { anchorRef: RefObject<HTMLElement | null>; id?: string }) {
|
||||
const [reduced, setReduced] = useState(false)
|
||||
const [frame, setFrame] = useState(1)
|
||||
const [duration, setDuration] = useState(0)
|
||||
const { panelRef, style } = useSWHeaderOverlay(anchorRef, 208, -84)
|
||||
useEffect(() => {
|
||||
const query = window.matchMedia("(prefers-reduced-motion: reduce)")
|
||||
const update = () => setReduced(query.matches)
|
||||
update(); query.addEventListener("change", update)
|
||||
return () => query.removeEventListener("change", update)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
setFrame(1); setDuration(0)
|
||||
if (reduced) return
|
||||
let timer: ReturnType<typeof setTimeout>
|
||||
let phase = 0
|
||||
// Delay + Figma transition, then a short natural return for the user-requested loop.
|
||||
const phases = [
|
||||
{ frame: 2, delay: 400, duration: 1022 },
|
||||
{ frame: 3, delay: 1122, duration: 1022 },
|
||||
{ frame: 4, delay: 1172, duration: 511 },
|
||||
{ frame: 1, delay: 911, duration: 511 },
|
||||
]
|
||||
const schedule = () => {
|
||||
const next = phases[phase]
|
||||
timer = setTimeout(() => {
|
||||
setDuration(next.duration); setFrame(next.frame)
|
||||
phase = (phase + 1) % phases.length
|
||||
if (phase === 0) {
|
||||
// Complete the return transition before starting the next 400ms initial hold.
|
||||
timer = setTimeout(schedule, 511)
|
||||
} else schedule()
|
||||
}, next.delay)
|
||||
}
|
||||
schedule()
|
||||
return () => clearTimeout(timer)
|
||||
}, [reduced])
|
||||
return (
|
||||
<div ref={panelRef} id={id} role="tooltip" className="hm-sw-k-guide__panel" data-frame={frame} data-reduced-motion={reduced || undefined} style={style}>
|
||||
<div className="hm-sw-k-guide__viewport" style={{ maskImage: `url("${frame === 4 ? fitMask : frame === 1 ? defaultMask : panMask}")` }}>
|
||||
<img src={guideImage} alt="" style={{ transitionDuration: `${duration}ms` }} />
|
||||
</div>
|
||||
<span>다중인쇄/도면관리</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import layerEllipse from "../../../assets/layer-ellipse.svg"
|
||||
import layerEllipseOff from "../../../assets/layer-ellipse-off.svg"
|
||||
import layerFreezeHover from "../../../assets/layer-freeze-hover.svg"
|
||||
import layerFreezeOff from "../../../assets/layer-freeze-off.svg"
|
||||
import layerFreezeOffHover from "../../../assets/layer-freeze-off-hover.svg"
|
||||
import layerLockHover from "../../../assets/layer-lock-hover.svg"
|
||||
import layerLockOff from "../../../assets/layer-lock-off.svg"
|
||||
import layerLockOffHover from "../../../assets/layer-lock-off-hover.svg"
|
||||
import layerStateA from "../../../assets/layer-state-a.svg"
|
||||
import layerStateB from "../../../assets/layer-state-b.svg"
|
||||
import layerUnion from "../../../assets/layer-union.svg"
|
||||
import type { SWHeaderLayer } from "../types"
|
||||
|
||||
/** 전구 icon_전구 26:10810 · 눈꽃 icon_눈꽃 26:10855 · 자물쇠 icon_자물쇠 26:10912 (20 x 20, 4 variants each). */
|
||||
export type SWHeaderLayerIconKind = "visibility" | "freeze" | "lock"
|
||||
|
||||
/** Figma Default / Variant2 (hover) = colored "on", Variant3 / Variant4 (hover) = gray "off". */
|
||||
const art = {
|
||||
freeze: { on: layerStateB, onHover: layerFreezeHover, off: layerFreezeOff, offHover: layerFreezeOffHover },
|
||||
lock: { on: layerStateA, onHover: layerLockHover, off: layerLockOff, offHover: layerLockOffHover },
|
||||
}
|
||||
|
||||
/**
|
||||
* Colored ("on") variant per layer state (user-confirmed 2026-10-16, design spec §5.2.1):
|
||||
* bulb visible, snowflake not frozen, lock not locked. Gray means hidden / frozen / locked.
|
||||
*/
|
||||
export function layerIconOn(kind: SWHeaderLayerIconKind, layer?: Pick<SWHeaderLayer, "visible" | "frozen" | "locked">) {
|
||||
if (kind === "visibility") return layer?.visible !== false
|
||||
if (kind === "freeze") return layer?.frozen !== true
|
||||
return layer?.locked !== true
|
||||
}
|
||||
|
||||
/** Static icon art. The bulb is the Figma composite (union + bars + ellipse); off swaps only the ellipse. */
|
||||
export function SWHeaderLayerIconArt({ kind, on }: { kind: SWHeaderLayerIconKind; on: boolean }) {
|
||||
if (kind === "visibility") {
|
||||
return (
|
||||
<span className="hm-sw-header__layer-icon hm-sw-header__layer-icon--composite">
|
||||
<img className="hm-sw-header__layer-union" src={layerUnion} alt="" />
|
||||
<span className="hm-sw-header__layer-bars"><i /><i /><i /><i /><i /></span>
|
||||
<img className="hm-sw-header__layer-ellipse" src={on ? layerEllipse : layerEllipseOff} alt="" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return <img className="hm-sw-header__layer-icon" src={on ? art[kind].on : art[kind].off} alt="" />
|
||||
}
|
||||
|
||||
const labels: Record<SWHeaderLayerIconKind, string> = { visibility: "표시", freeze: "동결", lock: "잠금" }
|
||||
|
||||
/**
|
||||
* One 20 x 20 state toggle of a layer row. The visual comes from the layer data only; hover is CSS
|
||||
* (bulb: black 25% background, snowflake / lock: their Figma hover asset). Without `onChange` it is a
|
||||
* status icon, so no click does nothing.
|
||||
*/
|
||||
export function SWHeaderLayerToggle({ kind, layer, onChange }: {
|
||||
kind: SWHeaderLayerIconKind
|
||||
layer: SWHeaderLayer
|
||||
onChange?: (layer: SWHeaderLayer, next: boolean) => void
|
||||
}) {
|
||||
const on = layerIconOn(kind, layer)
|
||||
// aria-pressed follows the data meaning (shown / frozen / locked), not the icon color.
|
||||
const pressed = kind === "visibility" ? on : !on
|
||||
const className = `hm-sw-header__layer-toggle hm-sw-header__layer-toggle--${kind}`
|
||||
const label = `${layer.name} ${labels[kind]}`
|
||||
if (!onChange) {
|
||||
return (
|
||||
<span className={className} data-on={on ? "true" : "false"} role="img" aria-label={`${label} ${pressed ? "켜짐" : "꺼짐"}`}>
|
||||
<SWHeaderLayerIconArt kind={kind} on={on} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={className}
|
||||
data-on={on ? "true" : "false"}
|
||||
aria-pressed={pressed}
|
||||
aria-label={label}
|
||||
disabled={layer.disabled}
|
||||
onClick={() => onChange(layer, !pressed)}
|
||||
>
|
||||
<SWHeaderLayerIconArt kind={kind} on={on} />
|
||||
{kind !== "visibility" && (
|
||||
<img className="hm-sw-header__layer-toggle-hover" src={on ? art[kind].onHover : art[kind].offHover} alt="" />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
import { useEffect, useId, useLayoutEffect, useRef, useState, type KeyboardEvent } from "react"
|
||||
import { SWHeaderMenuDetail } from "./SWHeaderDropdown"
|
||||
import type { SWHeaderLayer, SWHeaderLayerValue } from "../types"
|
||||
import { SWHeaderLayerControl } from "./SWHeaderFields"
|
||||
import { SWHeaderLayerToggle } from "./SWHeaderLayerIcons"
|
||||
|
||||
export interface SWHeaderLayerMenuProps {
|
||||
/** Name shown in the field when no `layers` entry matches `activeLayerId`. */
|
||||
label?: string
|
||||
value: SWHeaderLayerValue
|
||||
/** Rows of the dropdown, in this order. An empty array shows only the footer. */
|
||||
layers: SWHeaderLayer[]
|
||||
/** Current layer: shown in the field (name and state icons) and as a #FFC600 name in the list. */
|
||||
activeLayerId?: string
|
||||
/** Controlled open state. Leave undefined to let the component manage it. */
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
/** Called on every click of the field (also when the dropdown toggles). */
|
||||
onClick?: () => void
|
||||
/** Keeps the field in its selected (#FFC600) look while closed. */
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onLayerSelect?: (layer: SWHeaderLayer) => void
|
||||
onVisibilityChange?: (layer: SWHeaderLayer, nextVisible: boolean) => void
|
||||
onFreezeChange?: (layer: SWHeaderLayer, nextFrozen: boolean) => void
|
||||
onLockChange?: (layer: SWHeaderLayer, nextLocked: boolean) => void
|
||||
/** "레이어 상세". Optional host action; omitting it does not disable hover or simulate CAD work. */
|
||||
onDetail?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Layer field with its dropdown (Figma header/menu/레이어 26:10940, rows header/dropdown/brown/row
|
||||
* 26:10687). Click / Enter / Space toggle it (hover never opens it), ArrowDown opens it and focuses
|
||||
* the current row, Escape / a click outside / Tab out close it. Selecting a row closes it; the bulb,
|
||||
* snowflake and lock are sibling buttons of the row selection, so they never select and keep it open.
|
||||
*/
|
||||
export function SWHeaderLayerMenu({
|
||||
value,
|
||||
label,
|
||||
layers,
|
||||
activeLayerId,
|
||||
open: openProp,
|
||||
onOpenChange,
|
||||
onClick,
|
||||
selected = false,
|
||||
disabled = false,
|
||||
onLayerSelect,
|
||||
onVisibilityChange,
|
||||
onFreezeChange,
|
||||
onLockChange,
|
||||
onDetail,
|
||||
}: SWHeaderLayerMenuProps) {
|
||||
const [internalOpen, setInternalOpen] = useState(false)
|
||||
const open = !disabled && (openProp ?? internalOpen)
|
||||
const rootRef = useRef<HTMLDivElement>(null)
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
const triggerRef = useRef<HTMLButtonElement>(null)
|
||||
const menuId = useId()
|
||||
// Set when the keyboard opens the menu, so the current (or first) row gets focus once it renders.
|
||||
const focusRowRef = useRef(false)
|
||||
// Horizontal offset that keeps the 234px list inside the header (narrow screens only).
|
||||
const [offset, setOffset] = useState(0)
|
||||
|
||||
const setOpen = (next: boolean) => {
|
||||
if (openProp === undefined) setInternalOpen(next)
|
||||
onOpenChange?.(next)
|
||||
}
|
||||
const close = (restoreFocus: boolean) => {
|
||||
setOpen(false)
|
||||
if (restoreFocus) triggerRef.current?.focus()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!rootRef.current?.contains(event.target as Node)) setOpen(false)
|
||||
}
|
||||
document.addEventListener("pointerdown", onPointerDown)
|
||||
return () => document.removeEventListener("pointerdown", onPointerDown)
|
||||
})
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!open) return
|
||||
const update = () => {
|
||||
const root = rootRef.current
|
||||
const menu = menuRef.current
|
||||
if (!root || !menu) return
|
||||
const bounds = (root.closest(".hm-sw-header") ?? document.documentElement).getBoundingClientRect()
|
||||
// 1px inside the bounds so the outline ring (drawn 1px outside the list) never overflows.
|
||||
const left = Math.max(bounds.left, 0) + 1
|
||||
const right = Math.min(bounds.right, document.documentElement.clientWidth) - 1
|
||||
const start = root.getBoundingClientRect().left
|
||||
let next = Math.min(0, right - menu.offsetWidth - start)
|
||||
if (start + next < left) next = left - start
|
||||
setOffset(next > 0 ? Math.ceil(next) : Math.floor(next))
|
||||
}
|
||||
update()
|
||||
window.addEventListener("resize", update)
|
||||
return () => window.removeEventListener("resize", update)
|
||||
}) // Host-driven reordering can move an open dropdown without a window resize.
|
||||
|
||||
useEffect(() => {
|
||||
if (open && focusRowRef.current) {
|
||||
focusRowRef.current = false
|
||||
const current = rootRef.current?.querySelector<HTMLButtonElement>(".hm-sw-header__layer-row[data-active] .hm-sw-header__layer-item:not(:disabled)")
|
||||
;(current ?? grid()[0]?.[0])?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
/** Rows of focusable controls: each layer row (toggles + selection), then the footer. */
|
||||
const grid = () => Array.from(rootRef.current?.querySelectorAll<HTMLElement>(".hm-sw-header__layer-row, .hm-sw-header__layer-footer") ?? [])
|
||||
.map(row => Array.from(row.querySelectorAll<HTMLButtonElement>("button:not(:disabled)")))
|
||||
.filter(row => row.length > 0)
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === "Escape" && open) {
|
||||
event.stopPropagation()
|
||||
close(true)
|
||||
return
|
||||
}
|
||||
const vertical = event.key === "ArrowDown" || event.key === "ArrowUp"
|
||||
const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight"
|
||||
if (!vertical && !horizontal) return
|
||||
if (!open) {
|
||||
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return
|
||||
event.preventDefault()
|
||||
focusRowRef.current = true
|
||||
setOpen(true)
|
||||
return
|
||||
}
|
||||
const rows = grid()
|
||||
if (rows.length === 0) return
|
||||
const active = document.activeElement as HTMLButtonElement
|
||||
const rowIndex = rows.findIndex(row => row.includes(active))
|
||||
event.preventDefault()
|
||||
if (rowIndex < 0) {
|
||||
// Focus on the field: enter the list.
|
||||
if (vertical) rows[event.key === "ArrowDown" ? 0 : rows.length - 1][0]?.focus()
|
||||
return
|
||||
}
|
||||
const row = rows[rowIndex]
|
||||
const column = row.indexOf(active)
|
||||
if (horizontal) {
|
||||
row[(column + (event.key === "ArrowRight" ? 1 : -1) + row.length) % row.length]?.focus()
|
||||
return
|
||||
}
|
||||
const target = rows[(rowIndex + (event.key === "ArrowDown" ? 1 : -1) + rows.length) % rows.length]
|
||||
// Same column (bulb / snowflake / lock / name) in the next row; the footer has one button.
|
||||
target[Math.min(column, target.length - 1)]?.focus()
|
||||
}
|
||||
|
||||
const current = layers.find(item => item.id === activeLayerId)
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="hm-sw-header__layer"
|
||||
data-open={open ? "true" : undefined}
|
||||
onKeyDown={onKeyDown}
|
||||
onBlur={event => {
|
||||
// Tabbing out of the field and its dropdown closes it; focus is never trapped.
|
||||
if (open && !rootRef.current?.contains(event.relatedTarget as Node | null)) setOpen(false)
|
||||
}}
|
||||
>
|
||||
<SWHeaderLayerControl
|
||||
buttonRef={triggerRef}
|
||||
label={label}
|
||||
value={current ? { name: current.name } : value}
|
||||
state={current}
|
||||
selected={selected || open}
|
||||
expanded={open}
|
||||
controls={open ? menuId : undefined}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
onClick?.()
|
||||
setOpen(!open)
|
||||
}}
|
||||
/>
|
||||
{open && (
|
||||
<div
|
||||
ref={menuRef}
|
||||
id={menuId}
|
||||
className="hm-sw-header__layer-menu"
|
||||
role="group"
|
||||
aria-label="레이어 목록"
|
||||
data-shifted={offset !== 0 ? "true" : undefined}
|
||||
style={offset !== 0 ? { left: offset } : undefined}
|
||||
>
|
||||
<div className="hm-sw-header__layer-panel">
|
||||
{layers.length > 0 && (
|
||||
<ul className="hm-sw-header__layer-list">
|
||||
{layers.map(layer => (
|
||||
<li
|
||||
key={layer.id}
|
||||
className="hm-sw-header__layer-row"
|
||||
data-active={layer === current ? "true" : undefined}
|
||||
data-disabled={layer.disabled ? "true" : undefined}
|
||||
>
|
||||
<span className="hm-sw-header__layer-toggles">
|
||||
<SWHeaderLayerToggle kind="visibility" layer={layer} onChange={onVisibilityChange} />
|
||||
<SWHeaderLayerToggle kind="freeze" layer={layer} onChange={onFreezeChange} />
|
||||
<SWHeaderLayerToggle kind="lock" layer={layer} onChange={onLockChange} />
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="hm-sw-header__layer-item"
|
||||
title={layer.name}
|
||||
aria-current={layer === current ? "true" : undefined}
|
||||
disabled={layer.disabled}
|
||||
onClick={() => {
|
||||
onLayerSelect?.(layer)
|
||||
close(true)
|
||||
}}
|
||||
>
|
||||
<span className="hm-sw-header__layer-swatch" aria-hidden="true">
|
||||
<span style={{ background: layer.color ?? "#ffffff" }} />
|
||||
</span>
|
||||
<span className="hm-sw-header__layer-name">{layer.name}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<div className="hm-sw-header__layer-footer">
|
||||
<SWHeaderMenuDetail label="레이어 상세" className="hm-sw-header__layer-detail" disabled={disabled} onClick={onDetail && (() => { onDetail(); close(true) })} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import { useLayoutEffect, type CSSProperties } from "react"
|
||||
import type { SWHeaderLineStyleValue, SWHeaderLineType, SWHeaderLineWeight } from "../types"
|
||||
import { SWHeaderMenuDetail, useSWHeaderDropdown } from "./SWHeaderDropdown"
|
||||
import { SWHeaderLineStyle } from "./SWHeaderFields"
|
||||
import { SWHeaderInput, parseSWHeaderNumber } from "./SWHeaderInput"
|
||||
|
||||
/** Figma line type previews (header/menu/선스타일 26:11143): dash lengths of the 70px line, 4px gaps. */
|
||||
export const swHeaderLinePatterns = {
|
||||
/** CENTER: long 22 / short 7. */
|
||||
center: [22, 7, 22, 7],
|
||||
/** HIDDEN: two 33px dashes. */
|
||||
hidden: [33, 33],
|
||||
} as const satisfies Record<string, readonly number[]>
|
||||
|
||||
/** 70 x 2 line preview with a 1px black outline (Figma Rectangle 7532, stroke outside). */
|
||||
export function SWHeaderLinePreview({ pattern }: { pattern?: readonly number[] }) {
|
||||
const segments = pattern && pattern.length > 0 ? pattern : [1]
|
||||
return (
|
||||
<span className="hm-sw-header__line-preview" aria-hidden="true">
|
||||
{segments.map((length, index) => <i key={index} style={{ flexGrow: length }} />)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export interface SWHeaderLineStyleMenuProps {
|
||||
/** Values shown in the field (line style / weight / scale) and in the scale input. */
|
||||
label?: string
|
||||
value: SWHeaderLineStyleValue
|
||||
/** Line type column. Host data. */
|
||||
lineTypes?: SWHeaderLineType[]
|
||||
/** Line weight column. Host data. */
|
||||
lineWeights?: SWHeaderLineWeight[]
|
||||
/** Current line type; defaults to the row named `value.name`. */
|
||||
activeLineTypeId?: string
|
||||
/** Current line weight; defaults to the row named `value.weight`. */
|
||||
activeLineWeightId?: string
|
||||
/** Controlled open state. Leave undefined to let the component manage it. */
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
/** Called on every click of the field (also when the dropdown toggles). */
|
||||
onClick?: () => void
|
||||
/** Keeps the field in its selected (#FFC600) look while closed. */
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onLineTypeSelect?: (lineType: SWHeaderLineType) => void
|
||||
onLineWeightSelect?: (weight: SWHeaderLineWeight) => void
|
||||
onLineWeightValueChange?: (weight: SWHeaderLineWeight, value: string) => void
|
||||
onScaleChange?: (value: string) => void
|
||||
/** "선스타일 상세". Optional host action; omitting it keeps hover and performs no CAD action. */
|
||||
onDetail?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Line style field with its 3-column dropdown (Figma header/menu/선스타일 26:11104; line type rows
|
||||
* header/dropdown/brown/row 26:10702): line type 182 · weight 142 · scale 142, centered under the field.
|
||||
* Each column changes only its own property and the dropdown stays open. Rows keep their Figma hover
|
||||
* (#62533D, #FF5C00 text and line); selected rows retain the same emphasis after mouse leave.
|
||||
*/
|
||||
export function SWHeaderLineStyleMenu({
|
||||
value, label, lineTypes = [], lineWeights = [], activeLineTypeId, activeLineWeightId, open: openProp, onOpenChange, onClick,
|
||||
selected = false, disabled = false, onLineTypeSelect, onLineWeightSelect, onLineWeightValueChange, onScaleChange, onDetail,
|
||||
}: SWHeaderLineStyleMenuProps) {
|
||||
const { open, setOpen, close, panelRef, triggerRef, menuId, panelStyle, rootProps } = useSWHeaderDropdown({ open: openProp, onOpenChange, disabled, align: "center" })
|
||||
const currentType = lineTypes.find(item => (activeLineTypeId === undefined ? item.name === value.name : item.id === activeLineTypeId))
|
||||
const currentWeight = lineWeights.find(item => (activeLineWeightId === undefined ? item.name === value.weight : item.id === activeLineWeightId))
|
||||
|
||||
// A promoted field can shrink below 284px. Paint only its real contact width, without changing
|
||||
// the shared dropdown alignment or the rest of the yellow outline.
|
||||
useLayoutEffect(() => {
|
||||
const field = triggerRef.current
|
||||
const panel = panelRef.current
|
||||
if (!open || !field || !panel) return
|
||||
const update = () => panel.style.setProperty("--hm-sw-line-seam-width", `${field.getBoundingClientRect().width}px`)
|
||||
update()
|
||||
const observer = new ResizeObserver(update)
|
||||
observer.observe(field)
|
||||
return () => observer.disconnect()
|
||||
}, [open, triggerRef, panelRef])
|
||||
|
||||
return (
|
||||
<div {...rootProps} className="hm-sw-header__menu hm-sw-header__menu--line-style" data-menu="line-style" data-tier="aux">
|
||||
<SWHeaderLineStyle
|
||||
buttonRef={triggerRef}
|
||||
label={label}
|
||||
value={value}
|
||||
selected={selected || open}
|
||||
expanded={open}
|
||||
controls={open ? menuId : undefined}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
onClick?.()
|
||||
setOpen(!open)
|
||||
}}
|
||||
/>
|
||||
{open && (
|
||||
<div ref={panelRef} id={menuId} className="hm-sw-header__menu-panel hm-sw-header__line-menu" role="group" aria-label="선스타일"
|
||||
style={{ ...panelStyle, "--hm-sw-line-seam-left": `${-(panelStyle?.left ?? 0)}px` } as CSSProperties}>
|
||||
<div className="hm-sw-header__line-drop">
|
||||
<div className="hm-sw-header__line-column hm-sw-header__line-column--type">
|
||||
<ul className="hm-sw-header__menu-list" aria-label="선 종류">
|
||||
{lineTypes.map(item => (
|
||||
<li key={item.id} className="hm-sw-header__menu-row hm-sw-header__menu-row--line" data-active={item === currentType ? "true" : undefined} data-disabled={item.disabled ? "true" : undefined}>
|
||||
<button type="button" className="hm-sw-header__menu-item" title={item.name} aria-current={item === currentType ? "true" : undefined} disabled={item.disabled} onClick={() => onLineTypeSelect?.(item)}>
|
||||
<SWHeaderLinePreview pattern={item.pattern} />
|
||||
<span className="hm-sw-header__menu-text hm-sw-header__menu-name">{item.name}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="hm-sw-header__menu-footer">
|
||||
<SWHeaderMenuDetail label="선스타일 상세" onClick={onDetail && (() => { onDetail(); close(true) })} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hm-sw-header__line-column">
|
||||
<ul className="hm-sw-header__menu-list" aria-label="가중치">
|
||||
{lineWeights.map(item => (
|
||||
<li key={item.id} className="hm-sw-header__menu-row hm-sw-header__menu-row--weight" data-active={item === currentWeight ? "true" : undefined} data-disabled={item.disabled ? "true" : undefined}>
|
||||
<button type="button" className="hm-sw-header__menu-item" title={item.name} aria-current={item === currentWeight ? "true" : undefined} disabled={item.disabled} onClick={() => onLineWeightSelect?.(item)}>
|
||||
<span className="hm-sw-header__menu-text hm-sw-header__menu-name">{item.name}</span>
|
||||
{item.value !== undefined && !item.editable && <span className="hm-sw-header__menu-text hm-sw-header__menu-value">{item.value}</span>}
|
||||
</button>
|
||||
{item.editable && (
|
||||
// Sibling of the row button (no nested controls): editing never selects the row.
|
||||
<SWHeaderInput
|
||||
aria-label={`${item.name} 가중치`}
|
||||
valueType="number"
|
||||
value={item.value ?? ""}
|
||||
disabled={item.disabled}
|
||||
parse={draft => parseSWHeaderNumber(draft, { min: 0 })}
|
||||
onCommit={onLineWeightValueChange && (next => onLineWeightValueChange(item, next))}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="hm-sw-header__line-column">
|
||||
<div className="hm-sw-header__menu-row hm-sw-header__menu-row--weight">
|
||||
<span className="hm-sw-header__menu-label hm-sw-header__menu-text hm-sw-header__menu-name">스케일</span>
|
||||
<SWHeaderInput
|
||||
aria-label="스케일"
|
||||
valueType="number"
|
||||
value={value.scale}
|
||||
parse={draft => parseSWHeaderNumber(draft, { min: 0, minExclusive: true })}
|
||||
onCommit={onScaleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { useEffect, useId, useRef, useState, type KeyboardEvent } from "react"
|
||||
import { SWHeaderKGuidePreview } from "./SWHeaderKGuide"
|
||||
|
||||
export interface SWHeaderMoreItem {
|
||||
visible?: boolean
|
||||
id: string
|
||||
label: string
|
||||
/** Current value shown at the right (e.g. the line style name). */
|
||||
value?: string
|
||||
/**
|
||||
* Header width where the item appears: `aux` < 1800, `panel` < 1080, `narrow` < 900, `action` < 640.
|
||||
* `slot`: only while another field has taken this field's place (the layer field, see SWHeader).
|
||||
*/
|
||||
tier: "aux" | "panel" | "narrow" | "action" | "slot"
|
||||
/** Field this item stands for (`data-menu` of the field), used by the representative-slot CSS. */
|
||||
menu?: string
|
||||
/** Renders a checkable item. */
|
||||
checked?: boolean
|
||||
selected?: boolean
|
||||
disabled?: boolean
|
||||
onSelect?: () => void
|
||||
/** Reuse the K guide when its original button has moved into More. */
|
||||
guide?: boolean
|
||||
}
|
||||
|
||||
export interface SWHeaderMoreProps {
|
||||
items: SWHeaderMoreItem[]
|
||||
}
|
||||
|
||||
/** Overflow menu that keeps every moved control reachable. */
|
||||
export function SWHeaderMore({ items }: SWHeaderMoreProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const rootRef = useRef<HTMLDivElement>(null)
|
||||
const triggerRef = useRef<HTMLButtonElement>(null)
|
||||
const menuId = useId()
|
||||
const guideId = useId()
|
||||
const guideRef = useRef<HTMLButtonElement>(null)
|
||||
const [guideOpen, setGuideOpen] = useState(false)
|
||||
useEffect(() => { if (!open) setGuideOpen(false) }, [open])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!rootRef.current?.contains(event.target as Node)) setOpen(false)
|
||||
}
|
||||
document.addEventListener("pointerdown", onPointerDown)
|
||||
return () => document.removeEventListener("pointerdown", onPointerDown)
|
||||
}, [open])
|
||||
|
||||
const visibleItems = () => Array.from(rootRef.current?.querySelectorAll<HTMLButtonElement>('[role^="menuitem"]:not(:disabled)') ?? []).filter(item => item.offsetParent !== null)
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && open) {
|
||||
event.stopPropagation()
|
||||
setOpen(false)
|
||||
triggerRef.current?.focus()
|
||||
} else if ((event.key === "ArrowDown" || event.key === "ArrowUp") && open) {
|
||||
event.preventDefault()
|
||||
const entries = visibleItems()
|
||||
const index = entries.indexOf(document.activeElement as HTMLButtonElement)
|
||||
const next = event.key === "ArrowDown" ? (index + 1) % entries.length : (index - 1 + entries.length) % entries.length
|
||||
entries[next]?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={rootRef} className="hm-sw-header__more" onKeyDown={onKeyDown} onPointerLeave={() => setGuideOpen(false)}>
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
className="hm-sw-header__more-trigger"
|
||||
aria-label="더보기"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={open}
|
||||
aria-controls={open ? menuId : undefined}
|
||||
onClick={() => setOpen(value => !value)}
|
||||
>
|
||||
<svg width="16" height="4" viewBox="0 0 16 4" aria-hidden="true" focusable="false">
|
||||
<circle cx="2" cy="2" r="1.6" fill="currentColor" />
|
||||
<circle cx="8" cy="2" r="1.6" fill="currentColor" />
|
||||
<circle cx="14" cy="2" r="1.6" fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
{open && (
|
||||
<div id={menuId} role="menu" aria-label="추가 도구" className="hm-sw-header__more-menu">
|
||||
{items.map(item => (
|
||||
<button
|
||||
key={item.id}
|
||||
ref={item.guide ? guideRef : undefined}
|
||||
type="button"
|
||||
role={item.checked === undefined ? "menuitem" : "menuitemcheckbox"}
|
||||
aria-checked={item.checked}
|
||||
className="hm-sw-header__more-item"
|
||||
data-tier={item.tier}
|
||||
data-visible={item.visible}
|
||||
hidden={item.visible === false}
|
||||
data-menu={item.menu}
|
||||
data-selected={item.selected || item.checked ? "true" : undefined}
|
||||
disabled={item.disabled}
|
||||
aria-describedby={item.guide && guideOpen ? guideId : undefined}
|
||||
onPointerEnter={() => setGuideOpen(!!item.guide && !item.disabled)}
|
||||
onFocus={() => setGuideOpen(!!item.guide && !item.disabled)}
|
||||
onBlur={event => { if (!rootRef.current?.contains(event.relatedTarget as Node | null)) setGuideOpen(false) }}
|
||||
onClick={() => {
|
||||
item.onSelect?.()
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
{item.value && <span className="hm-sw-header__more-value">{item.value}</span>}
|
||||
</button>
|
||||
))}
|
||||
{guideOpen && <SWHeaderKGuidePreview anchorRef={guideRef} id={guideId} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useId } from "react"
|
||||
|
||||
export interface SWHeaderPropertiesProps {
|
||||
barOpen?: boolean
|
||||
panelOpen?: boolean
|
||||
panelId?: string
|
||||
disabled?: boolean
|
||||
onBarToggle?: () => void
|
||||
onPanelToggle?: () => void
|
||||
/** Individual parts (default true). Removed parts take no space; with one toggle the divider is removed. */
|
||||
showCaption?: boolean
|
||||
showBar?: boolean
|
||||
showPanel?: boolean
|
||||
}
|
||||
|
||||
/** `상세보기` caption above the 속성Bar / 속성창 toggle pair. */
|
||||
export function SWHeaderProperties({ barOpen = false, panelOpen = false, panelId, disabled, onBarToggle, onPanelToggle, showCaption = true, showBar = true, showPanel = true }: SWHeaderPropertiesProps) {
|
||||
const captionId = useId()
|
||||
if (!showCaption && !showBar && !showPanel) return null
|
||||
return (
|
||||
<div className="hm-sw-header__properties" role="group" aria-labelledby={showCaption ? captionId : undefined} aria-label={showCaption ? undefined : "상세보기"} data-partial={showCaption && showBar && showPanel ? undefined : ""}>
|
||||
{showCaption && <span id={captionId} className="hm-sw-header__properties-caption">상세보기</span>}
|
||||
{(showBar || showPanel) && <div className="hm-sw-header__properties-row">
|
||||
{showBar && <button type="button" className="hm-sw-header__toggle" aria-pressed={barOpen} data-selected={barOpen ? "true" : undefined} disabled={disabled} onClick={onBarToggle}>속성Bar</button>}
|
||||
{showBar && showPanel && <span className="hm-sw-header__properties-divider" aria-hidden="true" />}
|
||||
{showPanel && <button type="button" className="hm-sw-header__toggle" data-property-panel-trigger="" aria-pressed={panelOpen} aria-haspopup={panelId ? "dialog" : undefined} aria-expanded={panelId ? panelOpen : undefined} aria-controls={panelOpen ? panelId : undefined} data-selected={panelOpen ? "true" : undefined} disabled={disabled} onClick={onPanelToggle}>속성창</button>}
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useId, type HTMLAttributes } from "react"
|
||||
import { SWHeaderInput, parseSWHeaderColor, parseSWHeaderNumber } from "./SWHeaderInput"
|
||||
import type { SWHeaderProperty, SWHeaderPropertySelection } from "./SWHeaderPropertyPanel"
|
||||
|
||||
export interface SWHeaderPropertyBarProps extends HTMLAttributes<HTMLDivElement> {
|
||||
selection: SWHeaderPropertySelection
|
||||
/** Same host-owned records as the property panel; array order is retained unless order is set. */
|
||||
properties: readonly SWHeaderProperty[]
|
||||
onPropertyChange?: (property: SWHeaderProperty, value: string) => void
|
||||
readOnly?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
function BarField({ property, onPropertyChange, readOnly, disabled }: Pick<SWHeaderPropertyBarProps, "onPropertyChange" | "readOnly" | "disabled"> & { property: SWHeaderProperty }) {
|
||||
const id = useId()
|
||||
const editable = !readOnly && !disabled && !property.readOnly && !property.disabled && property.type !== "computed"
|
||||
const parse = (draft: string) => {
|
||||
if (property.validate && !property.validate(draft)) return null
|
||||
if (property.type === "number" || property.type === "slider") return parseSWHeaderNumber(draft, { min: property.min, max: property.max })
|
||||
if (property.type === "color") return parseSWHeaderColor(draft)?.value ?? null
|
||||
if (property.type === "select") return property.options?.some(option => option.id === draft && !option.disabled) ? draft : null
|
||||
return draft.trim() || null
|
||||
}
|
||||
return (
|
||||
<div className="hm-sw-property-bar__field" data-property-id={property.id}>
|
||||
<label htmlFor={id}>{property.name}</label>
|
||||
<SWHeaderInput id={id} value={property.mixed ? "다양함" : property.value}
|
||||
valueType={property.type === "number" || property.type === "slider" ? "number" : "text"}
|
||||
disabled={disabled || property.disabled} readOnly={!editable} parse={parse} commitOnUnmount={false}
|
||||
onCommit={editable && (onPropertyChange || property.onCommit) ? next => {
|
||||
property.onCommit?.(next, property)
|
||||
onPropertyChange?.(property, next)
|
||||
} : undefined} />
|
||||
{property.unit && <span>{property.unit}</span>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Figma 26:10492: a separate 28px editing strip; no CAD values or calculations are built in. */
|
||||
export function SWHeaderPropertyBar({ selection, properties, onPropertyChange, readOnly, disabled, className, ...rest }: SWHeaderPropertyBarProps) {
|
||||
return (
|
||||
<div {...rest} className={`hm-sw-property-bar${className ? ` ${className}` : ""}`} role="region" aria-label={rest["aria-label"] ?? "속성Bar"} tabIndex={0}>
|
||||
<div className="hm-sw-property-bar__content" key={selection.id}>
|
||||
<span className="hm-sw-property-bar__object" title={selection.title}>{selection.title}</span>
|
||||
<div className="hm-sw-property-bar__fields">
|
||||
{[...properties].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)).map(property => (
|
||||
<BarField key={property.id} property={property} onPropertyChange={onPropertyChange} readOnly={readOnly} disabled={disabled} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
import { useEffect, useId, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode, type RefObject } from "react"
|
||||
import circleGraphic from "../../../assets/property-circle.svg"
|
||||
import titleGraphic from "../../../assets/property-title.svg"
|
||||
import singleTitleGraphic from "../../../assets/property-title-single.svg"
|
||||
import footerGraphic from "../../../assets/property-footer.svg"
|
||||
import expandGraphic from "../../../assets/property-expand.svg"
|
||||
import toolDefault from "../../../assets/property-tool.svg"
|
||||
import toolHover from "../../../assets/property-tool-hover.svg"
|
||||
import toolActiveGraphic from "../../../assets/property-tool-active.svg"
|
||||
import closeDefault from "../../../assets/property-close.svg"
|
||||
import closeHover from "../../../assets/property-close-hover.svg"
|
||||
import { SWHeaderInput, parseSWHeaderColor, parseSWHeaderNumber } from "./SWHeaderInput"
|
||||
import { SWHeaderLinePreview } from "./SWHeaderLineStyleMenu"
|
||||
|
||||
export type SWHeaderPropertyType = "text" | "number" | "select" | "color" | "slider" | "computed"
|
||||
export interface SWHeaderProperty {
|
||||
id: string
|
||||
name: string
|
||||
value: string
|
||||
type: SWHeaderPropertyType
|
||||
groupId: string
|
||||
order?: number
|
||||
readOnly?: boolean
|
||||
disabled?: boolean
|
||||
mixed?: boolean
|
||||
unit?: string
|
||||
annotation?: string
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
options?: readonly { id: string; name: string; disabled?: boolean }[]
|
||||
/** Optional color swatch / original line pattern, supplied by the host. */
|
||||
swatch?: string
|
||||
linePattern?: readonly number[]
|
||||
linePreview?: boolean
|
||||
graphicSlot?: "circumference" | "diameter" | "radius" | "area"
|
||||
validate?: (draft: string) => boolean
|
||||
onCommit?: (value: string, property: SWHeaderProperty) => void
|
||||
}
|
||||
export interface SWHeaderPropertyGroup {
|
||||
id: string
|
||||
name?: string
|
||||
order?: number
|
||||
layout?: "rows" | "circle" | "position"
|
||||
/** Rows beyond this count are revealed by the expansion button. */
|
||||
collapsedCount?: number
|
||||
positionLabel?: string
|
||||
}
|
||||
export interface SWHeaderPropertySelection {
|
||||
/** Change whenever the selected objects change, even if their property ids match. */
|
||||
id: string
|
||||
title: string
|
||||
kind: "none" | "all" | "single"
|
||||
counts?: readonly { id: string; name: string; count: number; row?: number; column?: number }[]
|
||||
/** Host-owned return to the overall selection; no CAD selection is performed internally. */
|
||||
overviewTitle?: string
|
||||
}
|
||||
/** What a shape slot receives: the selection, its group and that group's properties (values stay in the rows). */
|
||||
export interface SWHeaderPropertyShapeContext {
|
||||
selection: SWHeaderPropertySelection
|
||||
group: SWHeaderPropertyGroup
|
||||
properties: readonly SWHeaderProperty[]
|
||||
}
|
||||
/**
|
||||
* Shape area of a `layout: "circle"` group (Figma 26:11932, 208 x 208 inside the 260px shape group).
|
||||
* undefined: the Figma circle drawing. null / false: the empty area (size, value labels and panel height
|
||||
* are kept). A node or render function: host content (e.g. a shape viewer), fitted into the area.
|
||||
*/
|
||||
export type SWHeaderPropertyShapeContent = ReactNode | ((context: SWHeaderPropertyShapeContext) => ReactNode)
|
||||
export interface SWHeaderPropertyPanelProps {
|
||||
selection: SWHeaderPropertySelection
|
||||
properties: readonly SWHeaderProperty[]
|
||||
groups: readonly SWHeaderPropertyGroup[]
|
||||
onPropertyChange?: (property: SWHeaderProperty, value: string) => void
|
||||
expandedGroupIds?: readonly string[]
|
||||
defaultExpandedGroupIds?: readonly string[]
|
||||
onExpandedGroupsChange?: (ids: string[]) => void
|
||||
toolActive?: boolean
|
||||
onToolActiveChange?: (active: boolean) => void
|
||||
toolLabel?: string
|
||||
onOverviewClick?: () => void
|
||||
/** Replaceable shape area; see SWHeaderPropertyShapeContent. Independent of the property values. */
|
||||
shapeContent?: SWHeaderPropertyShapeContent
|
||||
onClose?: () => void
|
||||
id?: string
|
||||
/** Supplied by SWHeader; standalone use can position the panel with style. */
|
||||
anchorRef?: RefObject<HTMLElement | null>
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
const ordered = <T extends { order?: number }>(items: readonly T[]) => [...items].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
||||
|
||||
/** Viewport-bound overlay position shared by the property tool window and K guide. No portal or dim. */
|
||||
export function useSWHeaderOverlay(anchorRef: RefObject<HTMLElement | null> | undefined, width: number, offset = 0) {
|
||||
const panelRef = useRef<HTMLDivElement>(null)
|
||||
const [style, setStyle] = useState<CSSProperties>({ visibility: anchorRef ? "hidden" : undefined })
|
||||
useLayoutEffect(() => {
|
||||
if (!anchorRef) return
|
||||
const update = () => {
|
||||
const anchor = anchorRef.current
|
||||
if (!anchor) return
|
||||
const header = anchor.closest(".hm-sw-header")
|
||||
const bounds = (header ?? document.documentElement).getBoundingClientRect()
|
||||
const preferred = width === 314 ? anchor.querySelector<HTMLElement>('[data-property-panel-trigger]') : null
|
||||
const more = width === 314 ? anchor.querySelector<HTMLElement>('.hm-sw-header__more-trigger') : null
|
||||
const target = (preferred?.getClientRects().length ? preferred : more ?? anchor).getBoundingClientRect()
|
||||
const viewportWidth = document.documentElement.clientWidth
|
||||
const actualWidth = Math.min(width, viewportWidth - 8, bounds.width - 8)
|
||||
const left = Math.max(Math.max(bounds.left, 0) + 4, Math.min(target.left + offset, Math.min(bounds.right, viewportWidth) - actualWidth - 4))
|
||||
const top = Math.max(4, Math.min(target.bottom, window.innerHeight - (width === 208 ? 153 : 34)))
|
||||
setStyle({ position: "fixed", left, top, width: actualWidth, maxHeight: Math.max(30, window.innerHeight - top - 4) })
|
||||
}
|
||||
update()
|
||||
const observer = new ResizeObserver(update)
|
||||
if (anchorRef.current) observer.observe(anchorRef.current.closest(".hm-sw-header") ?? anchorRef.current)
|
||||
window.addEventListener("resize", update)
|
||||
window.addEventListener("scroll", update, true)
|
||||
return () => { observer.disconnect(); window.removeEventListener("resize", update); window.removeEventListener("scroll", update, true) }
|
||||
}, [anchorRef, width, offset])
|
||||
return { panelRef, style }
|
||||
}
|
||||
|
||||
function PropertyEditor({ property, onChange }: { property: SWHeaderProperty; onChange?: SWHeaderPropertyPanelProps["onPropertyChange"] }) {
|
||||
const inputId = useId()
|
||||
const { type, value, disabled, mixed, readOnly, unit } = property
|
||||
const [draft, setDraft] = useState(value)
|
||||
useEffect(() => setDraft(value), [value, property.options, disabled, readOnly])
|
||||
const pendingRef = useRef<string | null>(null)
|
||||
useEffect(() => { pendingRef.current = null }, [value])
|
||||
const editable = !disabled && !readOnly && type !== "computed" && (!!onChange || !!property.onCommit)
|
||||
const parse = (draft: string) => {
|
||||
if (property.validate && !property.validate(draft)) return null
|
||||
if (type === "number" || type === "slider") return parseSWHeaderNumber(draft, { min: property.min, max: property.max })
|
||||
if (type === "color") return parseSWHeaderColor(draft)?.value ?? null
|
||||
if (type === "select") return property.options?.some(option => option.id === draft && !option.disabled) ? draft : null
|
||||
return draft.trim()
|
||||
}
|
||||
const commit = (next: string) => {
|
||||
if (!editable || next === value || next === pendingRef.current) return
|
||||
if (parse(next) === null) { setDraft(value); return }
|
||||
pendingRef.current = next
|
||||
property.onCommit?.(next, property)
|
||||
onChange?.(property, next)
|
||||
}
|
||||
const cancelDraft = () => setDraft(value)
|
||||
const nativeKeys = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (event.nativeEvent.isComposing) return
|
||||
if (event.key === "Enter") { event.preventDefault(); commit(draft) }
|
||||
if (event.key === "Escape" && draft !== value) { event.stopPropagation(); cancelDraft() }
|
||||
}
|
||||
return (
|
||||
<div className="hm-sw-property__row" data-property-id={property.id} data-type={type} data-disabled={disabled || undefined}>
|
||||
<label htmlFor={inputId} title={property.name}>{property.name}</label>
|
||||
<div className="hm-sw-property__value">
|
||||
{type === "select" ? (
|
||||
<select id={inputId} aria-label={property.name} value={mixed && draft === value ? "__mixed" : draft} disabled={!editable} onChange={event => setDraft(event.target.value)} onBlur={() => commit(draft)} onKeyDown={nativeKeys}>
|
||||
{mixed && <option value="__mixed" disabled>다양함</option>}
|
||||
{!mixed && !property.options?.some(option => option.id === value) && <option value={value}>{value}</option>}
|
||||
{property.options?.map(option => <option key={option.id} value={option.id} disabled={option.disabled}>{option.name}</option>)}
|
||||
</select>
|
||||
) : (
|
||||
<SWHeaderInput
|
||||
id={inputId} aria-label={property.name} value={mixed ? "다양함" : value}
|
||||
valueType={type === "number" || type === "slider" ? "number" : "text"}
|
||||
disabled={disabled} readOnly={!editable} parse={parse} commitOnUnmount={false}
|
||||
onCommit={editable ? commit : undefined}
|
||||
/>
|
||||
)}
|
||||
{property.annotation && <span className="hm-sw-property__unit">{property.annotation}</span>}
|
||||
{unit && type !== "slider" && <span className="hm-sw-property__unit">{unit}</span>}
|
||||
{type === "color" && <span className="hm-sw-property__swatch" aria-hidden="true"><i style={{ backgroundColor: property.swatch ?? parseSWHeaderColor(value)?.hex ?? "transparent" }} /></span>}
|
||||
{property.linePreview && <SWHeaderLinePreview pattern={property.linePattern} />}
|
||||
{type === "slider" && <input type="range" aria-label={`${property.name} 슬라이더`} min={property.min ?? 0} max={property.max ?? 100} step={property.step ?? 1} value={mixed ? property.min ?? 0 : draft} disabled={!editable} onChange={event => setDraft(event.target.value)} onBlur={() => commit(draft)} onKeyDown={nativeKeys} />}
|
||||
{unit && type === "slider" && <span className="hm-sw-property__unit">{unit}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** A single data-driven, non-modal property tool window (Figma 26:11822). */
|
||||
export function SWHeaderPropertyPanel({
|
||||
selection, properties, groups, onPropertyChange, expandedGroupIds, defaultExpandedGroupIds = [],
|
||||
onExpandedGroupsChange, toolActive: controlledTool, onToolActiveChange, toolLabel = "객체 선택 도구",
|
||||
onOverviewClick, shapeContent, onClose, id, anchorRef, style: customStyle,
|
||||
}: SWHeaderPropertyPanelProps) {
|
||||
const titleId = useId()
|
||||
const [expanded, setExpanded] = useState<string[]>([...defaultExpandedGroupIds])
|
||||
const [internalTool, setInternalTool] = useState(false)
|
||||
const active = controlledTool ?? internalTool
|
||||
const expandedIds = expandedGroupIds ?? expanded
|
||||
const { panelRef, style } = useSWHeaderOverlay(anchorRef, 314)
|
||||
useLayoutEffect(() => { if (anchorRef) panelRef.current?.focus() }, [anchorRef, panelRef])
|
||||
useEffect(() => {
|
||||
const onKey = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && !event.defaultPrevented && !event.isComposing) onClose?.()
|
||||
}
|
||||
document.addEventListener("keydown", onKey)
|
||||
return () => document.removeEventListener("keydown", onKey)
|
||||
}, [onClose])
|
||||
const toggleGroup = (groupId: string) => {
|
||||
const next = expandedIds.includes(groupId) ? expandedIds.filter(id => id !== groupId) : [...expandedIds, groupId]
|
||||
if (expandedGroupIds === undefined) setExpanded(next)
|
||||
onExpandedGroupsChange?.(next)
|
||||
}
|
||||
return (
|
||||
<div ref={panelRef} id={id} tabIndex={-1} className="hm-sw-property" data-selection-kind={selection.kind} role="dialog" aria-modal="false" aria-labelledby={titleId} style={{ ...style, ...customStyle }} onKeyDown={event => {
|
||||
if (event.key === "Escape" && !event.nativeEvent.isComposing) { event.stopPropagation(); onClose?.() }
|
||||
}}>
|
||||
<div className="hm-sw-property__titlebar">
|
||||
<img className="hm-sw-property__title-art" src={selection.kind === "single" ? singleTitleGraphic : titleGraphic} alt="" />
|
||||
<span id={titleId} className="hm-sw-property__title" title={selection.title}>{selection.title}</span>
|
||||
{selection.kind !== "single" && <button type="button" className="hm-sw-property__tool" aria-label={toolLabel} title={toolLabel} aria-pressed={active} onClick={() => {
|
||||
if (controlledTool === undefined) setInternalTool(!active)
|
||||
onToolActiveChange?.(!active)
|
||||
}}>
|
||||
<img className={active ? "hm-sw-property__tool-active" : "hm-sw-property__tool-default"} src={active ? toolActiveGraphic : toolDefault} alt="" />
|
||||
{!active && <img className="hm-sw-property__tool-hover" src={toolHover} alt="" />}
|
||||
</button>}
|
||||
<button type="button" className="hm-sw-property__close" aria-label="속성창 닫기" onClick={onClose}>
|
||||
<img className="hm-sw-property__close-default" src={closeDefault} alt="" />
|
||||
<img className="hm-sw-property__close-hover" src={closeHover} alt="" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="hm-sw-property__body" key={selection.id}>
|
||||
{ordered(groups).map(group => {
|
||||
const items = ordered(properties.filter(property => property.groupId === group.id))
|
||||
if (!items.length || (selection.kind === "none" && group.layout && group.layout !== "rows")) return null
|
||||
const isExpanded = expandedIds.includes(group.id)
|
||||
const expandable = group.collapsedCount !== undefined && items.length > group.collapsedCount
|
||||
const visible = expandable && !isExpanded ? items.slice(0, group.collapsedCount) : items
|
||||
return (
|
||||
<section key={group.id} className={`hm-sw-property__group hm-sw-property__group--${group.layout ?? "rows"}`} aria-label={group.name ?? "공통 속성"}>
|
||||
{group.name && <h3>{group.name}</h3>}
|
||||
<div className="hm-sw-property__group-content">
|
||||
{group.layout === "circle" && (() => {
|
||||
const content = typeof shapeContent === "function" ? shapeContent({ selection, group, properties: items }) : shapeContent
|
||||
const fallback = shapeContent === undefined
|
||||
return <div className="hm-sw-property__shape" data-shape-slot={fallback ? "default" : content === null || content === false ? "empty" : "custom"}>
|
||||
{fallback ? <img className="hm-sw-property__circle" src={circleGraphic} alt="원형 도형의 지름과 반지름 안내" /> : content}
|
||||
</div>
|
||||
})()}
|
||||
{group.layout === "position" && <div className="hm-sw-property__position-head"><span>구분</span>{visible.map(item => <span key={item.id}>{item.name}</span>)}</div>}
|
||||
<div className="hm-sw-property__editors">
|
||||
{group.layout === "position" && <span className="hm-sw-property__position-label">{group.positionLabel}</span>}
|
||||
{visible.map(property => <div key={property.id} className="hm-sw-property__editor" data-slot={property.graphicSlot}><PropertyEditor property={property} onChange={onPropertyChange} /></div>)}
|
||||
</div>
|
||||
{expandable && <button type="button" className="hm-sw-property__expand" aria-label={`${group.name ?? "공통 속성"} ${isExpanded ? "접기" : "펼치기"}`} aria-expanded={isExpanded} onClick={() => toggleGroup(group.id)}><img src={expandGraphic} alt="" /></button>}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
})}
|
||||
{selection.kind === "all" && !!selection.counts?.length && <div className="hm-sw-property__counts" aria-label="선택된 객체 종류와 개수">{selection.counts.map(item => <span key={item.id} style={{ gridColumn: item.column, gridRow: item.row }}>{item.name} {item.count}</span>)}</div>}
|
||||
</div>
|
||||
{selection.overviewTitle && <button type="button" className="hm-sw-property__overview" onClick={onOverviewClick} disabled={!onOverviewClick}><img src={footerGraphic} alt="" /><span>{selection.overviewTitle}</span></button>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import type { ReactNode, Ref } from "react"
|
||||
import dimensionRowIcon from "../../../assets/menu-dimension-row.svg"
|
||||
import dimensionRowIconHover from "../../../assets/menu-dimension-row-hover.svg"
|
||||
import type {
|
||||
SWHeaderDimensionStyleOption, SWHeaderDimensionStyleValue, SWHeaderTextStyleOption, SWHeaderTextStyleValue,
|
||||
} from "../types"
|
||||
import { SWHeaderMenuDetail, useSWHeaderDropdown, type SWHeaderDropdownOptions } from "./SWHeaderDropdown"
|
||||
import { SWHeaderDimensionStyle, SWHeaderTextStyle } from "./SWHeaderFields"
|
||||
|
||||
interface ListMenuBase extends Pick<SWHeaderDropdownOptions, "open" | "onOpenChange" | "disabled"> {
|
||||
/** Called on every click of the field (also when the dropdown toggles). */
|
||||
onClick?: () => void
|
||||
/** Keeps the field in its selected (#FFC600) look while closed. */
|
||||
label?: string
|
||||
selected?: boolean
|
||||
/** Optional footer host action. All detail buttons remain hoverable without a host callback. */
|
||||
onDetail?: () => void
|
||||
}
|
||||
|
||||
interface StyleListMenuProps<T extends { id: string; name: string; disabled?: boolean }> extends ListMenuBase {
|
||||
variant: "text-style" | "dimension-style"
|
||||
label: string
|
||||
items: T[]
|
||||
current?: T
|
||||
onSelect?: (item: T) => void
|
||||
/** Field button (receives the dropdown wiring). */
|
||||
field: (wiring: { buttonRef: Ref<HTMLButtonElement>; expanded: boolean; controls?: string; selected: boolean; disabled: boolean; onClick: () => void }) => ReactNode
|
||||
/** Row content before the name. */
|
||||
preview: (item: T) => ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Single-list style dropdown (text / dimension style): 164px rows of 30px with their Figma hover
|
||||
* (#62533D, #FF5C00), a 40px footer with the "상세" pill. Choosing a row reports it and closes the list.
|
||||
*/
|
||||
function StyleListMenu<T extends { id: string; name: string; disabled?: boolean }>({
|
||||
variant, label, items, current, onSelect, field, preview, open: openProp, onOpenChange, onClick, selected = false, disabled = false, onDetail,
|
||||
}: StyleListMenuProps<T>) {
|
||||
const { open, setOpen, close, panelRef, triggerRef, menuId, panelStyle, shifted, rootProps } = useSWHeaderDropdown({ open: openProp, onOpenChange, disabled })
|
||||
return (
|
||||
<div {...rootProps} className={`hm-sw-header__menu hm-sw-header__menu--${variant}`} data-menu={variant} data-tier="aux">
|
||||
{field({
|
||||
buttonRef: triggerRef,
|
||||
expanded: open,
|
||||
controls: open ? menuId : undefined,
|
||||
selected: selected || open,
|
||||
disabled,
|
||||
onClick: () => {
|
||||
onClick?.()
|
||||
setOpen(!open)
|
||||
},
|
||||
})}
|
||||
{open && (
|
||||
<div
|
||||
ref={panelRef}
|
||||
id={menuId}
|
||||
className={`hm-sw-header__menu-panel hm-sw-header__list-menu hm-sw-header__list-menu--${variant}`}
|
||||
role="group"
|
||||
aria-label={label}
|
||||
data-shifted={shifted ? "true" : undefined}
|
||||
style={panelStyle}
|
||||
>
|
||||
{items.length > 0 && (
|
||||
<ul className="hm-sw-header__menu-list">
|
||||
{items.map(item => (
|
||||
<li key={item.id} className="hm-sw-header__menu-row hm-sw-header__menu-row--style" data-active={item === current ? "true" : undefined} data-disabled={item.disabled ? "true" : undefined}>
|
||||
<button
|
||||
type="button"
|
||||
className="hm-sw-header__menu-item"
|
||||
title={item.name}
|
||||
aria-current={item === current ? "true" : undefined}
|
||||
disabled={item.disabled}
|
||||
onClick={() => {
|
||||
onSelect?.(item)
|
||||
close(true)
|
||||
}}
|
||||
>
|
||||
{preview(item)}
|
||||
<span className="hm-sw-header__menu-text hm-sw-header__menu-name">{item.name}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<div className="hm-sw-header__menu-footer">
|
||||
<SWHeaderMenuDetail label={`${label} 상세`} onClick={onDetail && (() => { onDetail(); close(true) })} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export interface SWHeaderTextStyleMenuProps extends ListMenuBase {
|
||||
value: SWHeaderTextStyleValue
|
||||
/** Rows (Figma: Arial / Standard, 돋움 / Dotum). Host data. */
|
||||
textStyles: SWHeaderTextStyleOption[]
|
||||
/** Current style; defaults to the row named `value.name`. */
|
||||
activeTextStyleId?: string
|
||||
onTextStyleSelect?: (style: SWHeaderTextStyleOption) => void
|
||||
}
|
||||
|
||||
/** Text style field with its dropdown (Figma header/menu/문자스타일 26:11181): 16px font sample + style name. */
|
||||
export function SWHeaderTextStyleMenu({ value, label = "문자스타일", textStyles, activeTextStyleId, onTextStyleSelect, ...rest }: SWHeaderTextStyleMenuProps) {
|
||||
const current = textStyles.find(item => (activeTextStyleId === undefined ? item.name === value.name : item.id === activeTextStyleId))
|
||||
return (
|
||||
<StyleListMenu
|
||||
{...rest}
|
||||
variant="text-style"
|
||||
label={label}
|
||||
items={textStyles}
|
||||
current={current}
|
||||
onSelect={onTextStyleSelect}
|
||||
field={wiring => <SWHeaderTextStyle label={label} value={value} {...wiring} />}
|
||||
preview={item => <span className="hm-sw-header__menu-text hm-sw-header__menu-font" title={item.font}>{item.font}</span>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export interface SWHeaderDimensionStyleMenuProps extends ListMenuBase {
|
||||
value: SWHeaderDimensionStyleValue
|
||||
/** Rows (Figma: Standard, ISO - 25). Host data. */
|
||||
dimensionStyles: SWHeaderDimensionStyleOption[]
|
||||
/** Current style; defaults to the row named `value.name`. */
|
||||
activeDimensionStyleId?: string
|
||||
onDimensionStyleSelect?: (style: SWHeaderDimensionStyleOption) => void
|
||||
}
|
||||
|
||||
/** Dimension style field with its dropdown (Figma header/menu/치수스타일 26:11215): dimension sample + style name. */
|
||||
export function SWHeaderDimensionStyleMenu({ value, label = "치수스타일", dimensionStyles, activeDimensionStyleId, onDimensionStyleSelect, ...rest }: SWHeaderDimensionStyleMenuProps) {
|
||||
const current = dimensionStyles.find(item => (activeDimensionStyleId === undefined ? item.name === value.name : item.id === activeDimensionStyleId))
|
||||
return (
|
||||
<StyleListMenu
|
||||
{...rest}
|
||||
variant="dimension-style"
|
||||
label={label}
|
||||
items={dimensionStyles}
|
||||
current={current}
|
||||
onSelect={onDimensionStyleSelect}
|
||||
field={wiring => <SWHeaderDimensionStyle label={label} value={value} {...wiring} />}
|
||||
preview={() => (
|
||||
// Figma row asset (#F6F4F2) and its hover variant (#FF5C00): only the foreground fill differs.
|
||||
<span className="hm-sw-header__menu-dimension" aria-hidden="true">
|
||||
<img className="hm-sw-header__menu-dimension-default" src={dimensionRowIcon} alt="" />
|
||||
<img className="hm-sw-header__menu-dimension-hover" src={dimensionRowIconHover} alt="" />
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { PointerEventHandler } from "react"
|
||||
import windowMotion from "../../../assets/window-motion.svg"
|
||||
import windowMotionWindow from "../../../assets/window-motion-window.svg"
|
||||
import windowMotionCursor from "../../../assets/window-motion-cursor.svg"
|
||||
import closeIcon from "../../../assets/close.svg"
|
||||
import closeIconHover from "../../../assets/close-hover.svg"
|
||||
|
||||
export interface SWHeaderWindowControlsProps {
|
||||
onWindowMoveStart?: PointerEventHandler<HTMLDivElement>
|
||||
onMinimize?: () => void
|
||||
onClose?: () => void
|
||||
/** Individual parts (default true). Removed parts take no space. */
|
||||
showWindowMove?: boolean
|
||||
showMinimize?: boolean
|
||||
showClose?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Window-move area plus minimize and close. Window behavior is owned by the host.
|
||||
* The move area plays the Figma "창이동 motion" hover (26:10220) in CSS only; its graphics and
|
||||
* tooltip ignore the pointer, so pointer down always reaches the area.
|
||||
*/
|
||||
export function SWHeaderWindowControls({ onWindowMoveStart, onMinimize, onClose, showWindowMove = true, showMinimize = true, showClose = true }: SWHeaderWindowControlsProps) {
|
||||
return (
|
||||
<>
|
||||
{showWindowMove && <div className="hm-sw-header__motion" data-sw-window-move="" role="presentation" onPointerDown={onWindowMoveStart}>
|
||||
<img className="hm-sw-header__motion-icon" src={windowMotion} alt="" aria-hidden="true" />
|
||||
<span className="hm-sw-header__motion-window" aria-hidden="true"><img src={windowMotionWindow} alt="" /></span>
|
||||
<span className="hm-sw-header__motion-cursor" aria-hidden="true"><img src={windowMotionCursor} alt="" /></span>
|
||||
<span className="hm-sw-header__motion-tooltip" role="tooltip">창 이동</span>
|
||||
</div>}
|
||||
{(showMinimize || showClose) && <div className="hm-sw-header__window-buttons" data-partial={showMinimize && showClose ? undefined : ""}>
|
||||
{showMinimize && <button type="button" className="hm-sw-header__window-button hm-sw-header__window-button--minimize" aria-label="최소화" onClick={onMinimize}>
|
||||
<span aria-hidden="true" />
|
||||
</button>}
|
||||
{showClose && <button type="button" className="hm-sw-header__window-button hm-sw-header__window-button--close" aria-label="종료" onClick={onClose}>
|
||||
<img className="hm-sw-header__icon" src={closeIcon} alt="" aria-hidden="true" />
|
||||
<img className="hm-sw-header__icon-hover" src={closeIconHover} alt="" aria-hidden="true" />
|
||||
</button>}
|
||||
</div>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
export { SWHeader } from "./SWHeader"
|
||||
export { SWHeaderHome, type SWHeaderHomeProps } from "./components/SWHeaderHome"
|
||||
export { SWHeaderDocumentTitle, type SWHeaderDocumentTitleProps } from "./components/SWHeaderDocumentTitle"
|
||||
export {
|
||||
SWHeaderLayerControl, SWHeaderColorControl, SWHeaderLineStyle,
|
||||
SWHeaderTextStyle, SWHeaderDimensionStyle, type SWHeaderLayerControlProps,
|
||||
} from "./components/SWHeaderFields"
|
||||
export { SWHeaderLayerMenu, type SWHeaderLayerMenuProps } from "./components/SWHeaderLayerMenu"
|
||||
export { SWHeaderColorMenu, type SWHeaderColorMenuProps } from "./components/SWHeaderColorMenu"
|
||||
export {
|
||||
SWHeaderLineStyleMenu, SWHeaderLinePreview, swHeaderLinePatterns, type SWHeaderLineStyleMenuProps,
|
||||
} from "./components/SWHeaderLineStyleMenu"
|
||||
export {
|
||||
SWHeaderTextStyleMenu, SWHeaderDimensionStyleMenu,
|
||||
type SWHeaderTextStyleMenuProps, type SWHeaderDimensionStyleMenuProps,
|
||||
} from "./components/SWHeaderStyleListMenus"
|
||||
export {
|
||||
SWHeaderInput, parseSWHeaderColor, parseSWHeaderNumber,
|
||||
type SWHeaderInputProps, type SWHeaderInputValueType,
|
||||
} from "./components/SWHeaderInput"
|
||||
export { SWHeaderProperties, type SWHeaderPropertiesProps } from "./components/SWHeaderProperties"
|
||||
export { SWHeaderPropertyPanel, type SWHeaderPropertyPanelProps, type SWHeaderProperty, type SWHeaderPropertyType, type SWHeaderPropertyGroup, type SWHeaderPropertySelection, type SWHeaderPropertyShapeContent, type SWHeaderPropertyShapeContext } from "./components/SWHeaderPropertyPanel"
|
||||
export { SWHeaderActions, type SWHeaderActionsProps } from "./components/SWHeaderActions"
|
||||
export { SWHeaderWindowControls, type SWHeaderWindowControlsProps } from "./components/SWHeaderWindowControls"
|
||||
export type {
|
||||
SWHeaderProps, SWHeaderMenuId, SWHeaderLayerValue, SWHeaderLayer, SWHeaderColorValue,
|
||||
SWHeaderLineStyleValue, SWHeaderTextStyleValue, SWHeaderDimensionStyleValue, SWHeaderDocument,
|
||||
SWHeaderColorMode, SWHeaderColorOption, SWHeaderColorInputValue, SWHeaderLineType, SWHeaderLineWeight,
|
||||
SWHeaderTextStyleOption, SWHeaderDimensionStyleOption,
|
||||
} from "./types"
|
||||
|
||||
export { SWHeaderPropertyBar, type SWHeaderPropertyBarProps } from "./components/SWHeaderPropertyBar"
|
||||
export type { SWHeaderControlId, SWHeaderControlConfig, SWHeaderFrameControlId, SWHeaderHideableControlId } from "./types"
|
||||
@@ -0,0 +1,329 @@
|
||||
import type { HTMLAttributes, PointerEventHandler, ReactNode } from "react"
|
||||
import type { SWHeaderPropertyBarProps } from "./components/SWHeaderPropertyBar"
|
||||
import type { SWHeaderPropertyPanelProps } from "./components/SWHeaderPropertyPanel"
|
||||
|
||||
/** Header controls that can be shown as selected (text and arrow turn #FFC600). */
|
||||
export type SWHeaderMenuId = "layer" | "color" | "lineStyle" | "textStyle" | "dimensionStyle"
|
||||
|
||||
export interface SWHeaderLayerValue {
|
||||
/** Current layer name. */
|
||||
name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* One row of the layer dropdown (Figma header/menu/레이어 26:10940). The host program owns the data:
|
||||
* the toggles only report the next value through callbacks; the header never changes a layer itself.
|
||||
*/
|
||||
export interface SWHeaderLayer {
|
||||
/** Stable key, returned with every callback. */
|
||||
id: string
|
||||
/** Layer name (ellipsis when long). */
|
||||
name: string
|
||||
/** CSS color of the 16 x 16 swatch. Defaults to `#ffffff`. */
|
||||
color?: string
|
||||
/** Shown: colored bulb when true, gray when false. A click reports `!visible`. Default `true`. */
|
||||
visible?: boolean
|
||||
/** Frozen: gray snowflake when true, colored (thawed) when false. A click reports `!frozen`. Default `false`. */
|
||||
frozen?: boolean
|
||||
/** Locked: gray lock when true, colored (unlocked) when false. A click reports `!locked`. Default `false`. */
|
||||
locked?: boolean
|
||||
/** Disables selecting the row and its three toggles. */
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export interface SWHeaderColorValue {
|
||||
/** Display text, e.g. `255,229,0`. */
|
||||
value: string
|
||||
/** CSS color of the swatch. Defaults to `#ffe500`. */
|
||||
swatch?: string
|
||||
}
|
||||
|
||||
export interface SWHeaderLineStyleValue {
|
||||
name: string
|
||||
/** Line weight text, e.g. `0.25`. */
|
||||
weight: string
|
||||
/** Line scale text, e.g. `1.00`. */
|
||||
scale: string
|
||||
}
|
||||
|
||||
export interface SWHeaderTextStyleValue {
|
||||
/** Font name shown at the left of the field, e.g. `Arial`. */
|
||||
font: string
|
||||
/** Text style name, e.g. `Standard`. */
|
||||
name: string
|
||||
}
|
||||
|
||||
/** One entry of the document dropdown (no file type or product assumed). */
|
||||
export interface SWHeaderDocument {
|
||||
/** Stable key, returned with the selection. */
|
||||
id: string
|
||||
/** Display name (ellipsis when long). */
|
||||
name: string
|
||||
/** Tooltip; defaults to `name` (e.g. a full path). */
|
||||
title?: string
|
||||
/** Icon in the 29 x 12 slot; defaults to `documentIcon` or the Figma document icon. */
|
||||
icon?: ReactNode
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export interface SWHeaderDimensionStyleValue {
|
||||
name: string
|
||||
}
|
||||
|
||||
/** ByLayer / ByBlock pair of the color dropdown (Figma ByLayer 26:12666, ByBlock 26:12659). */
|
||||
export type SWHeaderColorMode = "byLayer" | "byBlock"
|
||||
|
||||
/** One 22px chip of the color dropdown (Figma color chip 22px 26:11091). The host owns the palette. */
|
||||
export interface SWHeaderColorOption {
|
||||
/** Stable key, returned with the selection. */
|
||||
id: string
|
||||
/** CSS color of the chip. */
|
||||
color: string
|
||||
/** Value text reported with the selection, e.g. `255,0,0`. Defaults to the RGB text of `color`. */
|
||||
value?: string
|
||||
/** Accessible name, e.g. `빨강`. Defaults to `value`. */
|
||||
label?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/** A color typed into the color dropdown input, validated before it is reported. */
|
||||
export interface SWHeaderColorInputValue {
|
||||
/** Normalized `r,g,b` text, e.g. `255,229,0`. */
|
||||
value: string
|
||||
rgb: [number, number, number]
|
||||
/** `#rrggbb`. */
|
||||
hex: string
|
||||
}
|
||||
|
||||
/**
|
||||
* One row of the line type column (Figma header/dropdown/brown/row 26:10702). `pattern` lists the dash
|
||||
* lengths of the 70px preview (4px gaps, scaled to fill 70px); omit it for a solid line.
|
||||
* `swHeaderLinePatterns` holds the Figma CENTER / HIDDEN patterns.
|
||||
*/
|
||||
export interface SWHeaderLineType {
|
||||
id: string
|
||||
name: string
|
||||
pattern?: readonly number[]
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/** One row of the line weight column. `value` is shown at the right (ByDefault); `editable` shows it in an input (User). */
|
||||
export interface SWHeaderLineWeight {
|
||||
id: string
|
||||
name: string
|
||||
value?: string
|
||||
editable?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/** One row of the text style dropdown (font sample 16px + style name). */
|
||||
export interface SWHeaderTextStyleOption {
|
||||
id: string
|
||||
/** Style name, e.g. `Standard`. */
|
||||
name: string
|
||||
/** Font name shown at the left, e.g. `Arial`. */
|
||||
font: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/** One row of the dimension style dropdown (dimension sample + style name). */
|
||||
export interface SWHeaderDimensionStyleOption {
|
||||
id: string
|
||||
name: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/** Optional complete allowlist. Omitted => approved default layout; [] => no optional controls.
|
||||
* Array order controls rendered order. Stable ids preserve data and component state on reordering. */
|
||||
export type SWHeaderControlId = "document" | SWHeaderMenuId | "drawingMode" | "details" | "propertyBar" | "propertyPanel" | "print" | "popupGuide"
|
||||
/** Common frame parts outside the object area. Their position is fixed; each can be removed. */
|
||||
export type SWHeaderFrameControlId = "home" | "windowMove" | "minimize" | "close"
|
||||
/** Every Header part except the logo, which is always shown. */
|
||||
export type SWHeaderHideableControlId = SWHeaderControlId | SWHeaderFrameControlId
|
||||
export interface SWHeaderControlConfig {
|
||||
id: SWHeaderControlId
|
||||
visible?: boolean
|
||||
label?: string
|
||||
}
|
||||
|
||||
export interface SWHeaderProps extends Omit<HTMLAttributes<HTMLElement>, "title" | "children" | "color"> {
|
||||
/** File name shown in the document area (ellipsis when narrow). */
|
||||
documentTitle: string
|
||||
controls?: readonly SWHeaderControlConfig[]
|
||||
/**
|
||||
* Parts to remove, in either layout (default design or `controls`). Works without passing a full
|
||||
* `controls` list, so the default design and order of the remaining parts are kept. Removing
|
||||
* "home" keeps the logo as a static image. Unknown ids are ignored. Default: none.
|
||||
*/
|
||||
hiddenControls?: readonly SWHeaderHideableControlId[]
|
||||
/**
|
||||
* Order of the similar menus (layer / color / lineStyle / textStyle / dimensionStyle). They swap
|
||||
* places only among their own slots; other parts never move. Listed ids come first (unknown and
|
||||
* duplicate ids are ignored), missing ids follow in the default order. Omitted: approved default
|
||||
* order (or the `controls` order). Order never changes values or calls a callback.
|
||||
*/
|
||||
menuOrder?: readonly SWHeaderMenuId[]
|
||||
/**
|
||||
* Documents of the dropdown under the file name (Figma 26:10662). When omitted the file name
|
||||
* stays a plain button (no dropdown); an empty array shows only New / Open.
|
||||
*/
|
||||
documents?: SWHeaderDocument[]
|
||||
/** Controlled open state of the document dropdown; uncontrolled when undefined. */
|
||||
documentMenuOpen?: boolean
|
||||
onDocumentMenuOpenChange?: (open: boolean) => void
|
||||
/** A document row was chosen. Opening it is up to the host. */
|
||||
onDocumentSelect?: (document: SWHeaderDocument) => void
|
||||
/** Current document id: its name is #FFC600 in the dropdown. The list keeps the `documents` order. */
|
||||
activeDocumentId?: string
|
||||
/**
|
||||
* Close Document: an X ("문서 닫기") on every document row, shown on row hover or keyboard focus.
|
||||
* The host closes that document and updates `documents` / `activeDocumentId`; the header does
|
||||
* not choose the next document.
|
||||
*/
|
||||
onDocumentClose?: (document: SWHeaderDocument) => void
|
||||
/** "+ New" in the dropdown footer. */
|
||||
onDocumentNew?: () => void
|
||||
/** "Open" in the dropdown footer. */
|
||||
onDocumentOpen?: () => void
|
||||
/** Default icon for documents without `icon`. Defaults to the Figma document icon. */
|
||||
documentIcon?: ReactNode
|
||||
/** Footer labels. Default `+ New` / `Open` (Figma). */
|
||||
documentNewLabel?: string
|
||||
documentOpenLabel?: string
|
||||
/** Label of the drawing-mode area. Defaults to `그리기모드`. */
|
||||
drawingModeLabel?: string
|
||||
/** Current layer shown in the field (used when no `layers` entry matches `activeLayerId`). */
|
||||
layer?: SWHeaderLayerValue
|
||||
/**
|
||||
* Layers of the dropdown under the layer field (Figma 26:10940). When omitted the field stays a
|
||||
* plain button without a dropdown (the previous behavior); an empty array shows only the footer.
|
||||
*/
|
||||
layers?: SWHeaderLayer[]
|
||||
/** Current layer id: shown in the field (name/state icons); its row retains the hover emphasis. */
|
||||
activeLayerId?: string
|
||||
/** Controlled open state of the layer dropdown; uncontrolled when undefined. */
|
||||
layerMenuOpen?: boolean
|
||||
onLayerMenuOpenChange?: (open: boolean) => void
|
||||
/** A layer row (swatch / name) was chosen. The dropdown closes. */
|
||||
onLayerSelect?: (layer: SWHeaderLayer) => void
|
||||
/** Bulb toggle. Without it the bulb is a status icon only. The dropdown stays open. */
|
||||
onLayerVisibilityChange?: (layer: SWHeaderLayer, nextVisible: boolean) => void
|
||||
/** Snowflake toggle. Without it the snowflake is a status icon only. The dropdown stays open. */
|
||||
onLayerFreezeChange?: (layer: SWHeaderLayer, nextFrozen: boolean) => void
|
||||
/** Lock toggle. Without it the lock is a status icon only. The dropdown stays open. */
|
||||
onLayerLockChange?: (layer: SWHeaderLayer, nextLocked: boolean) => void
|
||||
/** "레이어 상세" in the dropdown footer. Optional host action; hover remains enabled without it. */
|
||||
onLayerDetail?: () => void
|
||||
color?: SWHeaderColorValue
|
||||
lineStyle?: SWHeaderLineStyleValue
|
||||
textStyle?: SWHeaderTextStyleValue
|
||||
dimensionStyle?: SWHeaderDimensionStyleValue
|
||||
|
||||
/**
|
||||
* Basic palette of the color dropdown (Figma header/menu/색상 26:10986). When omitted the color
|
||||
* field stays a plain button (the previous behavior). The header never changes the color itself:
|
||||
* every control only reports through its callback and the host updates `color` / `colorMode`.
|
||||
*/
|
||||
colors?: SWHeaderColorOption[]
|
||||
/** User colors, shown in extra 24px rows below the palette (Figma 26:11036). Any count. */
|
||||
userColors?: SWHeaderColorOption[]
|
||||
/** Color mode. null still means explicit color; the required indicator retains the last mode (default ByLayer). */
|
||||
colorMode?: SWHeaderColorMode | null
|
||||
/** Controlled open state of the color dropdown; uncontrolled when undefined. */
|
||||
colorMenuOpen?: boolean
|
||||
onColorMenuOpenChange?: (open: boolean) => void
|
||||
/** A palette or user color chip was clicked. The dropdown stays open. */
|
||||
onColorSelect?: (option: SWHeaderColorOption) => void
|
||||
/** ByLayer / ByBlock was clicked. The dropdown stays open. */
|
||||
onColorModeChange?: (mode: SWHeaderColorMode) => void
|
||||
/** A valid `r,g,b` (0-255) or `#rrggbb` was committed in the color input (Enter or leaving the input). */
|
||||
onColorInput?: (value: SWHeaderColorInputValue) => void
|
||||
/** Color pick button (Figma btn_colorize). Picking from the drawing is the host's job; the dropdown closes. */
|
||||
onColorPick?: () => void
|
||||
/** "색상 상세". Without it the button keeps its Figma look, disabled. The detail popup is not part of the header. */
|
||||
onColorDetail?: () => void
|
||||
|
||||
/** Line type column of the line style dropdown (Figma header/menu/선스타일 26:11104). When omitted (and no `lineWeights`) the field stays a plain button. */
|
||||
lineTypes?: SWHeaderLineType[]
|
||||
/** Line weight column (ByLayer / ByBlock / ByDefault / User ...). */
|
||||
lineWeights?: SWHeaderLineWeight[]
|
||||
/** Current line type; defaults to the row named `lineStyle.name`. */
|
||||
activeLineTypeId?: string
|
||||
/** Current line weight; defaults to the row named `lineStyle.weight`. */
|
||||
activeLineWeightId?: string
|
||||
lineStyleMenuOpen?: boolean
|
||||
onLineStyleMenuOpenChange?: (open: boolean) => void
|
||||
/** A line type row was clicked. Weight and scale are not touched. The dropdown stays open. */
|
||||
onLineTypeSelect?: (lineType: SWHeaderLineType) => void
|
||||
/** A line weight row was clicked. Line type and scale are not touched. The dropdown stays open. */
|
||||
onLineWeightSelect?: (weight: SWHeaderLineWeight) => void
|
||||
/** A valid non-negative number was committed in an editable weight row (User). */
|
||||
onLineWeightValueChange?: (weight: SWHeaderLineWeight, value: string) => void
|
||||
/** A valid positive number was committed in the scale input. */
|
||||
onLineScaleChange?: (value: string) => void
|
||||
/** "선스타일 상세". Without it the button keeps its Figma look, disabled. */
|
||||
onLineStyleDetail?: () => void
|
||||
|
||||
/** Rows of the text style dropdown (Figma header/menu/문자스타일 26:11181). When omitted the field stays a plain button. */
|
||||
textStyles?: SWHeaderTextStyleOption[]
|
||||
/** Current text style; defaults to the row named `textStyle.name`. */
|
||||
activeTextStyleId?: string
|
||||
textStyleMenuOpen?: boolean
|
||||
onTextStyleMenuOpenChange?: (open: boolean) => void
|
||||
/** A text style row was chosen. The dropdown closes. */
|
||||
onTextStyleSelect?: (style: SWHeaderTextStyleOption) => void
|
||||
/** "문자스타일 상세". Without it the button keeps its Figma look, disabled. */
|
||||
onTextStyleDetail?: () => void
|
||||
|
||||
/** Rows of the dimension style dropdown (Figma header/menu/치수스타일 26:11215). When omitted the field stays a plain button. */
|
||||
dimensionStyles?: SWHeaderDimensionStyleOption[]
|
||||
/** Current dimension style; defaults to the row named `dimensionStyle.name`. */
|
||||
activeDimensionStyleId?: string
|
||||
dimensionStyleMenuOpen?: boolean
|
||||
onDimensionStyleMenuOpenChange?: (open: boolean) => void
|
||||
/** A dimension style row was chosen. The dropdown closes. */
|
||||
onDimensionStyleSelect?: (style: SWHeaderDimensionStyleOption) => void
|
||||
/** "치수스타일 상세". Optional host action; hover remains enabled without it. */
|
||||
onDimensionStyleDetail?: () => void
|
||||
|
||||
/** Which menu is selected (open) in the host program. */
|
||||
activeMenu?: SWHeaderMenuId | null
|
||||
propertyBarOpen?: boolean
|
||||
defaultPropertyBarOpen?: boolean
|
||||
propertyBar?: SWHeaderPropertyBarProps
|
||||
onPropertyBarOpenChange?: (open: boolean) => void
|
||||
propertyPanelOpen?: boolean
|
||||
/** Additive property tool window configuration. Values and object changes belong to the host. */
|
||||
propertyPanel?: Omit<SWHeaderPropertyPanelProps, "onClose" | "anchorRef" | "id" | "style">
|
||||
/** Open/close notification for the tool window. Existing onPropertyPanelToggle is preserved. */
|
||||
onPropertyPanelOpenChange?: (open: boolean) => void
|
||||
/** Disables the menu / property / action groups (window controls stay enabled). */
|
||||
disabled?: boolean
|
||||
/** Extra tools rendered inside the object area, after the menus. */
|
||||
tools?: ReactNode
|
||||
/**
|
||||
* Logo image URL of the SW program, contained in the 32 x 17 logo slot.
|
||||
* The logo is decided by each program: none is rendered when omitted.
|
||||
*/
|
||||
logoSrc?: string
|
||||
/** Logo element (inline SVG, <img>, ...). Takes precedence over `logoSrc`. */
|
||||
logo?: ReactNode
|
||||
/** Accessible product name; the home button is announced as "<logoAlt> 홈" (default "홈"). */
|
||||
logoAlt?: string
|
||||
|
||||
onHomeClick?: () => void
|
||||
onDocumentClick?: () => void
|
||||
onLayerOpen?: () => void
|
||||
onColorOpen?: () => void
|
||||
onLineStyleOpen?: () => void
|
||||
onTextStyleOpen?: () => void
|
||||
onDimensionStyleOpen?: () => void
|
||||
onPropertyBarToggle?: () => void
|
||||
onPropertyPanelToggle?: () => void
|
||||
onPrint?: () => void
|
||||
onPopupGuide?: () => void
|
||||
/** Pointer down on the window-move area (the host decides how to move the window). */
|
||||
onWindowMoveStart?: PointerEventHandler<HTMLDivElement>
|
||||
onMinimize?: () => void
|
||||
onClose?: () => void
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { forwardRef, type HTMLAttributes } from "react"
|
||||
|
||||
export type SWThemeProps = HTMLAttributes<HTMLDivElement>
|
||||
|
||||
/**
|
||||
* Scope for SW sub-components used outside <SWHeader>: provides the SW tokens,
|
||||
* typography and control reset. Override any `--hm-sw-header-*` token through `style`.
|
||||
*/
|
||||
export const SWTheme = forwardRef<HTMLDivElement, SWThemeProps>(function SWTheme({ className, ...rest }, ref) {
|
||||
return <div ref={ref} className={`hm-sw-theme${className ? ` ${className}` : ""}`} {...rest} />
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
import "./theme/sw.css"
|
||||
|
||||
export * from "./components/sw-header"
|
||||
export { SWTheme, type SWThemeProps } from "./components/sw-theme/SWTheme"
|
||||
@@ -0,0 +1,164 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import http from 'node:http'
|
||||
|
||||
// Usage: node packages/sw/tests/detail-buttons.browser.mjs baseline|verify <evidence-dir> [base-url]
|
||||
const mode = process.argv[2] ?? 'verify'
|
||||
const output = path.resolve(process.argv[3] ?? 'docs/sw/detail-button-qa')
|
||||
let base = process.argv[4] ?? 'static'
|
||||
let server
|
||||
if (base === 'static') {
|
||||
const root=path.resolve('storybook-static'),mime={'.html':'text/html','.js':'text/javascript','.css':'text/css','.json':'application/json','.svg':'image/svg+xml','.ttf':'font/ttf'}
|
||||
server=http.createServer(async(req,res)=>{try{const p=new URL(req.url,'http://localhost').pathname,file=path.resolve(root,'.'+decodeURIComponent(p==='/'?'/index.html':p));if(path.relative(root,file).startsWith('..'))return res.writeHead(403).end();res.setHeader('Content-Type',mime[path.extname(file)]??'application/octet-stream');res.end(await readFile(file))}catch{res.writeHead(404).end()}})
|
||||
await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));base=`http://127.0.0.1:${server.address().port}`
|
||||
}
|
||||
const { chromium } = createRequire(import.meta.url)(process.env.SW_PLAYWRIGHT_PATH ?? 'C:/Users/user/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright')
|
||||
await mkdir(output, { recursive: true })
|
||||
const browser = await chromium.launch({ headless: true, executablePath: process.env.SW_CHROME_PATH ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe' })
|
||||
const page = await browser.newPage({ viewport: { width: 1920, height: 1000 } })
|
||||
const report = { mode, browser: browser.version(), checks: [], errors: [], styles: {} }
|
||||
page.on('pageerror', e => report.errors.push(e.message))
|
||||
page.on('console', m => { if (m.type() === 'error') report.errors.push(m.text()) })
|
||||
const check = (name, ok, data) => { report.checks.push({ name, status: ok ? 'PASS' : 'FAIL', data }); if (!ok) console.error('FAIL', name, data) }
|
||||
const goto = async id => {
|
||||
await page.goto(`${base}/iframe.html?id=sw-header--${id}&viewMode=story`)
|
||||
await page.locator('.hm-sw-header, .hm-sw-theme').first().waitFor()
|
||||
await page.evaluate(() => document.fonts.ready)
|
||||
await page.mouse.move(1, 1)
|
||||
}
|
||||
const style = e => {
|
||||
const s = getComputedStyle(e), t = e.querySelector('[data-detail-label],span:last-child'), i = e.querySelector('img')
|
||||
return { background:s.backgroundColor,color:s.color,opacity:s.opacity,border:s.border, radius:s.borderRadius,shadow:s.boxShadow,
|
||||
textColor:getComputedStyle(t).color,textOpacity:getComputedStyle(t).opacity,textStroke:getComputedStyle(t).webkitTextStrokeWidth,
|
||||
iconOpacity:i && getComputedStyle(i).opacity,iconContent:i && getComputedStyle(i).content,
|
||||
height:e.getBoundingClientRect().height,width:e.getBoundingClientRect().width,outline:s.outline,disabled:e.disabled }
|
||||
}
|
||||
const snapshots = {}
|
||||
try {
|
||||
if (mode === 'capture') {
|
||||
await goto('detail-buttons')
|
||||
await page.screenshot({path:path.join(output,'comparison-default.png'),fullPage:true})
|
||||
await page.locator('[data-detail-row="문자스타일 상세"] [data-detail-case="hover"] button').hover()
|
||||
await page.screenshot({path:path.join(output,'comparison-pointer-hover.png'),fullPage:true})
|
||||
} else if (mode === 'preservation') {
|
||||
await goto('controls')
|
||||
for(const width of [390,768,1280,1440]){
|
||||
await page.setViewportSize({width,height:1000})
|
||||
const layout=await page.locator('header.hm-sw-header').first().evaluate(h=>({height:h.getBoundingClientRect().height,overflow:h.scrollWidth-h.clientWidth,documentOverflow:document.documentElement.scrollWidth-document.documentElement.clientWidth}))
|
||||
check(`required responsive width ${width}`,layout.height===49&&layout.overflow===0&&layout.documentOverflow===0,layout)
|
||||
}
|
||||
await page.setViewportSize({width:1920,height:1000})
|
||||
for(const [id,height] of [['none',256],['all',361],['circle',658],['expanded',754]]){
|
||||
await goto(`property-panel-${id}`)
|
||||
const box=await page.locator('.hm-sw-property').first().boundingBox()
|
||||
check(`preserve property panel ${id}`,box.width===314&&box.height===height,box)
|
||||
}
|
||||
await goto('controls')
|
||||
const motion=page.locator('[data-sw-window-move]').first();await motion.hover()
|
||||
check('preserve window motion 1400ms',await motion.evaluate(e=>e.getAnimations({subtree:true}).length===3&&e.getAnimations({subtree:true}).every(a=>a.effect.getTiming().duration===1400)))
|
||||
await page.mouse.move(1,900);check('window motion leave clears animations',await motion.evaluate(e=>e.getAnimations({subtree:true}).length===0))
|
||||
const guide=page.locator('.hm-sw-k-guide').first();await guide.locator('button').hover()
|
||||
check('preserve K guide animation',await guide.locator('.hm-sw-k-guide__panel').isVisible()&&await guide.locator('.hm-sw-k-guide__panel').evaluate(e=>getComputedStyle(e).animationDuration==='0.511s'))
|
||||
await page.mouse.move(1,900);check('K guide leave closes',await guide.locator('.hm-sw-k-guide__panel').count()===0)
|
||||
await goto('colorize');const colorHeader=page.locator('header.hm-sw-header').first();await colorHeader.getByRole('button',{name:'색상',exact:true}).click()
|
||||
check('preserve Colorize size',(await colorHeader.getByRole('button',{name:'색상 추출',exact:true}).boundingBox()).width===24)
|
||||
await goto('layer-detail-input');const editing=page.getByRole('textbox',{name:'Editing',exact:true})
|
||||
check('preserve editable Input',await editing.isEditable()&&await editing.evaluate(e=>getComputedStyle(e).caretColor==='rgb(230, 4, 3)'))
|
||||
} else {
|
||||
const index = await (await fetch(`${base}/index.json`)).json()
|
||||
const ids = Object.values(index.entries).filter(e => e.title === 'SW/Header' && e.type === 'story').map(e => e.id)
|
||||
report.storyIds = ids
|
||||
if (mode === 'baseline') await writeFile(path.join(output, 'baseline-story-ids.json'), JSON.stringify(ids))
|
||||
for (const story of ['default', 'controls']) {
|
||||
await goto(story)
|
||||
for (const width of [1920,1800,1440,1180,1080,900,768,640,520,390]) {
|
||||
await page.setViewportSize({ width, height:1000 })
|
||||
snapshots[`${story}-${width}`] = await page.locator('header.hm-sw-header').first().evaluate(h => [h,...h.querySelectorAll('*')].map(e => {
|
||||
const b=e.getBoundingClientRect(),s=getComputedStyle(e)
|
||||
return {tag:e.tagName,class:e.getAttribute('class'),x:b.x,y:b.y,width:b.width,height:b.height,display:s.display,visibility:s.visibility,color:s.color,background:s.backgroundColor}
|
||||
}))
|
||||
}
|
||||
}
|
||||
await page.setViewportSize({ width:1920,height:1000 })
|
||||
await goto('text-dimension-style-dropdown')
|
||||
const original = page.getByRole('button',{name:'문자스타일 상세',exact:true}).first()
|
||||
snapshots.textDefault = await original.evaluate(style)
|
||||
if (!(await original.isDisabled())) { await original.hover(); snapshots.textHover = await original.evaluate(style) }
|
||||
else {
|
||||
// Existing callback-enabled example supplies the effective text-style Hover reference.
|
||||
const enabled = page.locator('button[data-detail="문자스타일 상세"]:not(:disabled)').first()
|
||||
await enabled.hover(); snapshots.textHover = await enabled.evaluate(style)
|
||||
}
|
||||
if (mode === 'baseline') {
|
||||
await writeFile(path.join(output,'baseline.json'),JSON.stringify(snapshots,null,2))
|
||||
} else {
|
||||
const before=JSON.parse(await readFile(path.join(output,'baseline.json'),'utf8'))
|
||||
for(const key of Object.keys(before).filter(k=>!k.startsWith('text'))) check(`unchanged header ${key}`,JSON.stringify(before[key])===JSON.stringify(snapshots[key]))
|
||||
const oldIds=JSON.parse(await readFile(path.join(output,'baseline-story-ids.json'),'utf8'))
|
||||
check('all previous 31 stories retained',oldIds.length===31&&oldIds.every(id=>ids.includes(id)),{before:oldIds.length,after:ids.length})
|
||||
for(const id of ids){await goto(id.replace('sw-header--',''));check(`render ${id}`,!(await page.locator('.sb-errordisplay').isVisible()))}
|
||||
await goto('composition')
|
||||
const presets=['기본 전체','구성 A','구성 B (레이어·선스타일)','구성 C (로고만)','파일 목록 제거','레이어 제거','색상 제거','스타일 그룹 제거','여러 메뉴 제거','순서 변경','숨김 + 순서 변경']
|
||||
const all=['layer','color','lineStyle','textStyle','dimensionStyle']
|
||||
const removed=[[],['textStyle','dimensionStyle'],['color','textStyle','dimensionStyle'],all,[],['layer'],['color'],['lineStyle','textStyle','dimensionStyle'],['color','textStyle'],[],['layer','textStyle']]
|
||||
const orders=[...Array(9).fill(all),['color','layer','dimensionStyle','lineStyle','textStyle'],['dimensionStyle','textStyle','color','layer','lineStyle']]
|
||||
for(let p=0;p<presets.length;p++)for(const width of [1920,1440,1080,900,768,640,390]){
|
||||
await page.setViewportSize({width,height:1000});await page.getByRole('button',{name:presets[p],exact:true}).click()
|
||||
const h=page.locator('[data-composition-demo] header.hm-sw-header')
|
||||
const actual=await h.evaluate(h=>({height:h.getBoundingClientRect().height,overflow:h.scrollWidth-h.clientWidth,menus:[...h.querySelectorAll('.hm-sw-header__fields > *')].filter(e=>e.querySelector('.hm-sw-header__field')).map(e=>e.dataset.control??({layer:'layer',color:'color','line-style':'lineStyle','text-style':'textStyle','dimension-style':'dimensionStyle'}[e.dataset.menu]??(e.classList.contains('hm-sw-header__layer')?'layer':null)))}))
|
||||
const expected=orders[p].filter(id=>!removed[p].includes(id))
|
||||
check(`composition ${presets[p]} @${width}`,actual.height===49&&actual.overflow===0&&JSON.stringify(actual.menus)===JSON.stringify(expected),{actual,expected})
|
||||
const more=h.locator('.hm-sw-header__more-trigger')
|
||||
if(await more.isVisible()) {await more.click();const items=await h.locator('.hm-sw-header__more-item').allTextContents();check(`nonempty More ${presets[p]} @${width}`,items.length>0);await page.keyboard.press('Escape')}
|
||||
}
|
||||
await page.setViewportSize({width:1920,height:1000})
|
||||
// Real dropdown integration, separate from the shared-button specimen.
|
||||
await goto('color-dropdown')
|
||||
const host=page.locator('section').filter({has:page.getByRole('heading',{name:'선택 · 입력 · Callback 기록',exact:true})})
|
||||
for(const [name,callback] of [['색상','onColorDetail'],['선스타일','onLineStyleDetail'],['문자스타일','onTextStyleDetail'],['치수스타일','onDimensionStyleDetail']]){
|
||||
const trigger=host.getByRole('button',{name,exact:true});await trigger.click()
|
||||
const detail=host.getByRole('button',{name:`${name} 상세`,exact:true});await detail.hover()
|
||||
check(`actual ${name} shared hover`,(await detail.evaluate(style)).textOpacity==='0.35')
|
||||
if(name!=='색상'){
|
||||
const row=host.locator('.hm-sw-header__menu-row').first();await row.locator('button').first().hover()
|
||||
check(`unchanged ${name} list hover`,await row.evaluate(e=>getComputedStyle(e).backgroundColor)==='rgb(98, 83, 61)')
|
||||
}
|
||||
await detail.click();check(`actual ${name} callback / close / focus`,(await host.locator('[data-callback-log]').textContent()).includes(callback)&&await trigger.getAttribute('aria-expanded')==='false'&&await trigger.evaluate(e=>e===document.activeElement))
|
||||
}
|
||||
await goto('layer-detail-input')
|
||||
const layerHost=page.locator('section').filter({has:page.getByRole('heading',{name:'레이어 상세 Click Callback',exact:true})})
|
||||
const layerTrigger=layerHost.getByRole('button',{name:'레이어',exact:true});await layerTrigger.click()
|
||||
await layerHost.getByRole('button',{name:'레이어 상세',exact:true}).click()
|
||||
check('actual layer callback / close / focus',(await layerHost.locator('[data-callback-log]').textContent()).includes('onLayerDetail')&&await layerTrigger.getAttribute('aria-expanded')==='false'&&await layerTrigger.evaluate(e=>e===document.activeElement))
|
||||
await goto('detail-buttons')
|
||||
const rows=page.locator('[data-detail-comparison] [data-detail-row]')
|
||||
const names=['레이어 상세','색상 상세','선스타일 상세','문자스타일 상세','치수스타일 상세'], widths=[113,99,128,142,142]
|
||||
for(let j=0;j<5;j++){
|
||||
const row=rows.nth(j),button=row.locator('[data-detail-case="default"] button'),off=row.locator('[data-detail-case="disabled"] button'),empty=row.locator('[data-detail-case="no-callback"] button')
|
||||
await page.mouse.move(1,1);const rest=await button.evaluate(style)
|
||||
await button.hover();const hover=await button.evaluate(style)
|
||||
await page.mouse.move(1,1);const leave=await button.evaluate(style)
|
||||
await off.hover({force:true});const disabled=await off.evaluate(style)
|
||||
report.styles[names[j]]={default:rest,hover,disabled}
|
||||
for(const [state,s,ref] of [['default',rest,before.textDefault],['hover',hover,before.textHover]]){
|
||||
check(`${names[j]} ${state} equals original text design`,['background','color','opacity','border','radius','shadow','textColor','textOpacity','textStroke','iconOpacity','iconContent'].every(k=>s[k]===ref[k]),{actual:s,reference:ref})
|
||||
check(`${names[j]} ${state} dimensions`,s.width===widths[j]&&s.height===24,s)
|
||||
}
|
||||
check(`${names[j]} leave restores`,JSON.stringify(rest)===JSON.stringify(leave))
|
||||
check(`${names[j]} disabled stays default`,disabled.disabled&&['background','textOpacity','iconContent','radius'].every(k=>disabled[k]===rest[k]))
|
||||
const count=await row.locator('output').textContent();const box=await off.boundingBox();await page.mouse.click(box.x+box.width/2,box.y+box.height/2)
|
||||
check(`${names[j]} disabled blocks callback`,await row.locator('output').textContent()===count)
|
||||
await button.click();check(`${names[j]} callback once`,await row.locator('output').textContent()==='1')
|
||||
await empty.hover();check(`${names[j]} callback-free hover`,!(await empty.isDisabled())&&(await empty.evaluate(style)).background===hover.background);await empty.click()
|
||||
await page.mouse.move(1,1);await button.focus();await page.keyboard.press('Tab');await page.keyboard.press('Shift+Tab')
|
||||
check(`${names[j]} keyboard focus visible`,await button.evaluate(e=>e.matches(':focus-visible')&&getComputedStyle(e).outlineStyle!=='none'))
|
||||
await page.keyboard.press('Enter');check(`${names[j]} keyboard callback`,await row.locator('output').textContent()==='2')
|
||||
}
|
||||
await page.mouse.move(1,1);await page.screenshot({path:path.join(output,'comparison-default.png'),fullPage:true})
|
||||
await rows.nth(3).locator('[data-detail-case="hover"] button').hover();await page.screenshot({path:path.join(output,'comparison-pointer-hover.png'),fullPage:true})
|
||||
}
|
||||
}
|
||||
check('no browser errors',report.errors.length===0,report.errors)
|
||||
} catch(e) { check('suite completed',false,e.stack) }
|
||||
finally { report.status=report.checks.every(c=>c.status==='PASS')?'PASS':'FAIL';await writeFile(path.join(output,`${mode}-result.json`),JSON.stringify(report,null,2));await browser.close();if(server)await new Promise(resolve=>server.close(resolve));console.log(JSON.stringify({status:report.status,checks:report.checks.length,stories:report.storyIds?.length,errors:report.errors}));process.exitCode=report.status==='PASS'?0:1 }
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"skipLibCheck": false,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"types": ["react", "react-dom"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
||||
"paths": {}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||