这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions client/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { VComponent } from '@/modules/v-component'
import { Z_INDEX } from '@/utils/constant'
import { Z_INDEX, NOOP } from '@/utils/constant'
import { EventBus, EVENT_KEY } from '@/event-bus'
import VToast from '@atoms/toast/toast'
import VConfirmDialog from '@molecules/confirm-dialog/confirm-dialog'

const template = document.createElement('template')
template.innerHTML = `
Expand Down Expand Up @@ -33,8 +34,8 @@ template.innerHTML = `
<v-canvas-container></v-canvas-container>
<v-canvas-toolbox></v-canvas-toolbox>
<v-history-toolbox></v-history-toolbox>

<v-toast variant="success" open="false" autoclose="true" title="" description=""></v-toast>
<v-confirm-dialog open="false" content=""></v-confirm-dialog>
</main>
</v-mobile-layout>
`
Expand All @@ -47,22 +48,35 @@ export default class App extends VComponent {
}

private $toast!: VToast
private $dialog!: VConfirmDialog

protected afterCreated() {
this.initToastElement()
}

private initToastElement() {
const $toast = this.$root.querySelector<VToast>('v-toast')
if (!$toast) {
const $dialog = this.$root.querySelector<VConfirmDialog>('v-confirm-dialog')
if (!$toast || !$dialog) {
throw new Error('initialize fail')
}

this.$toast = $toast
this.$dialog = $dialog
}

protected bindEventListener() {
this.$dialog.addEventListener('dialog:confirm', this.handleClearConfirm)
this.$dialog.addEventListener('dialog:cancel', NOOP)
}

private handleClearConfirm() {
EventBus.getInstance().emit(EVENT_KEY.CLEAR_ALL)
}

subscribeEventBus() {
EventBus.getInstance().on(EVENT_KEY.SHOW_TOAST, this.onShowToast.bind(this))
EventBus.getInstance().on(EVENT_KEY.SHOW_CONFIRM, this.onShowConfirmDialog.bind(this))
}

private onShowToast(variant: string, title: string, description: string) {
Expand All @@ -79,4 +93,9 @@ export default class App extends VComponent {
this.$toast.title = title
this.$toast.description = description
}

private onShowConfirmDialog(content: string) {
this.$dialog.content = content
this.$dialog.open = 'true'
}
}
12 changes: 0 additions & 12 deletions client/src/components/atoms/button/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,4 @@ describe('button', () => {

expect(buttonElement).toHaveTextContent('test')
})

it('should accept color attribute', async () => {
await renderToHtml(`
<v-button data-testid="button" color="red">test</v-button>
`)

const buttonElement = screen.getByTestId('button')
const rootElement = getTemplateRootElement<HTMLButtonElement>(buttonElement)
const style = await getInitialStyle(rootElement)

expect(style.color).toBe('red')
})
})
10 changes: 6 additions & 4 deletions client/src/components/atoms/button/button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ export default {
}

interface Props {
color: string
mode: 'fill' | 'outline'
variant: 'primary'
children: string
onClick: () => void
}

export const Basic = ({ color, children, onClick }: Props) =>
html`<v-button color="${color}" @click="${onClick}">${children}</v-button>`
export const Basic = ({ mode, variant, children, onClick }: Props) =>
html`<v-button mode="${mode}" variant="${variant}" @click="${onClick}">${children}</v-button>`
Basic.args = {
color: 'blue',
mode: 'fill',
variant: 'primary',
children: '예시 버튼',
onClick: () => console.log('clicked!'),
}
58 changes: 47 additions & 11 deletions client/src/components/atoms/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,29 @@ template.innerHTML = `
:host {
display: block;
}

:host button {
width: 100%;
border-radius: 16px;
border: none;
cursor: pointer;
padding: 13px 16px;
font-size: 14px;
line-height: 14px;
}

:host button[data-variant="primary"][data-mode="fill"] {
color: var(--color-white);
background-color: var(--color-azure);
}

:host button[data-variant="primary"][data-mode="outline"] {
color: var(--color-azure);
background-color: var(--color-azure15);
}

</style>
<button>
<button data-mode="outline" data-variant="primary">
<slot></slot>
</button>
`
Expand All @@ -21,29 +42,44 @@ export default class VButton extends VComponent {
}

static get observedAttributes() {
return ['color']
return ['mode', 'variant']
}

get color() {
return this.getAttribute('color') || 'red'
get mode() {
return this.getAttribute('mode') || 'fill'
}
set color(newValue: string) {
this.setAttribute('color', newValue)
set mode(newValue: string) {
this.setAttribute('mode', newValue)
}

get variant() {
return this.getAttribute('variant') || 'primary'
}
set variant(newValue: string) {
this.setAttribute('variant', newValue)
}

bindInitialProp() {
this.reflectAttribute({ attribute: 'color', value: this.color })
this.reflectAttribute({ attribute: 'mode', value: this.mode })
this.reflectAttribute({ attribute: 'variant', value: this.variant })
}

protected reflectAttribute({ attribute, value }: ReflectAttributeParam) {
switch (attribute) {
case 'color':
this.updateColorStyle(value)
case 'mode':
this.updateMode(value)
break
case 'variant':
this.updateVariant(value)
break
}
}

private updateColorStyle(value: string) {
this.$root.style.color = value
private updateMode(value: string) {
this.$root.dataset.mode = value
}

private updateVariant(value: string) {
this.$root.dataset.variant = value
}
}
36 changes: 36 additions & 0 deletions client/src/components/atoms/dialog/dialog.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { screen, waitFor } from '@testing-library/dom'
import { renderToHtml, getTemplateRootElement, getInitialStyle } from '@/modules/wc-test-utils'
import VDialog from './dialog'

describe('dialog', () => {
beforeAll(() => {
/**
* 💡 Use mocked method until jsdom support HTMLDialogElement
* @reference
* https://github.com/jsdom/jsdom/issues/3294#issuecomment-1196577616
*/
HTMLDialogElement.prototype.showModal = jest.fn(function (this: HTMLDialogElement) {
this.open = false
})
HTMLDialogElement.prototype.close = jest.fn(function (this: HTMLDialogElement) {
this.open = false
})
})

it('should render dialog with open="true"', async () => {
await renderToHtml(`
<v-dialog data-testid="dialog" open="false">
<div slot="title">다이얼로그</div>
</v-dialog>
`)

const $dialog = screen.getByTestId<VDialog>('dialog')
const $root = getTemplateRootElement<HTMLDialogElement>($dialog)

expect($root).not.toBeVisible()

$dialog.open = 'true'

waitFor(() => expect($root).toBeVisible())
})
})
15 changes: 15 additions & 0 deletions client/src/components/atoms/dialog/dialog.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { html } from 'lit-html'

export default {
title: 'Elements / Dialog',
}

export const Basic = () => html`
<v-dialog>
<div slot="title">다이얼로그</div>
<div slot="content">다이얼로그 내용</div>
<div slot="action">
<button>확인</button>
</div>
</v-dialog>
`
94 changes: 94 additions & 0 deletions client/src/components/atoms/dialog/dialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { VComponent } from '@/modules/v-component'
import type { ReflectAttributeParam } from '@/modules/v-component/types'

const template = document.createElement('template')
template.innerHTML = `
<style>
:host dialog {
display: flex;
flex-direction: column;
background: var(--color-white);
border-radius: 20px;
border: none;
filter: drop-shadow(0px 9px 17px rgba(125, 159, 192, 0.12));
width: 200px;
padding: 32px;
}

:host .dialog-title {
color: var(--color-black);
font-weight: 600;
font-size: 18px;
text-align: center;
margin: 0 0 20px;
}

:host .dialog-content {
color: var(--color-davy-gray);
font-weight: 500;
font-size: 14px;
line-height: 18px;
margin: 0 0 10px;
}

:host .dialog-action {
margin-top: 10px;
}

:host ::slotted([slot="action"]) {
display: flex;
flex-direction: row;
gap: 10px;
}
</style>
<dialog>
<h1 class="dialog-title">
<slot name="title"></slot>
</h1>
<section class="dialog-content">
<slot name="content"></slot>
</section>
<section class="dialog-action">
<slot name="action"></slot>
</section>
</dialog>
`

export default class VDialog extends VComponent<HTMLDialogElement> {
static tag = 'v-dialog'

constructor() {
super(template)
}

static get observedAttributes() {
return ['open']
}

get open() {
return this.getAttribute('open') === 'true' ? 'true' : 'false'
}
set open(newValue: 'true' | 'false') {
this.setAttribute('open', newValue)
}

protected bindInitialProp() {
this.reflectAttribute({ attribute: 'open', value: this.open })
}

protected reflectAttribute({ attribute, value }: ReflectAttributeParam) {
switch (attribute) {
case 'open':
this.updateOpenStyle(value)
break
}
}

private updateOpenStyle(value: string) {
if (value === 'true') {
this.$root.showModal()
} else {
this.$root.close()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ template.innerHTML = `
grid-auto-flow: column;
grid-template-rows: repeat(2, 1fr);
gap: 10px 15px;
width: 220px;
width: 200px;
height: 270px;
overflow-x: scroll;
}
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/molecules/canvas-layer/image-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ function findAnchorInPath({
}

function drawDeleteAnchor({ context, point }: { context: CanvasRenderingContext2D; point: Point }) {
const ANCHOR_RADIUS = 24
const ANCHOR_RADIUS = 26

const path2d = drawCircle({
context,
Expand All @@ -485,7 +485,7 @@ function drawDeleteAnchor({ context, point }: { context: CanvasRenderingContext2
}

function drawResizeAnchor({ context, point }: { context: CanvasRenderingContext2D; point: Point }) {
const ANCHOR_RADIUS = 24
const ANCHOR_RADIUS = 26

const path2d = drawCircle({
context,
Expand Down
Loading