스타일 가이드 적용 완료. UTKCOlorPicker, UTKDatePicker 확인해야 함
This commit is contained in:
@@ -37,16 +37,19 @@
|
||||
=================================== */
|
||||
|
||||
.utk-foldout > .unity-foldout__toggle > .unity-toggle__input > .unity-foldout__checkmark {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: var(--space-s);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
-unity-background-image-tint-color: var(--color-text-secondary);
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: rotate;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
margin-right: var(--space-xs);
|
||||
}
|
||||
|
||||
.utk-foldout > .unity-foldout__toggle:checked > .unity-toggle__input > .unity-foldout__checkmark {
|
||||
rotate: 90deg;
|
||||
rotate: 0deg;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -57,13 +60,15 @@
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
-unity-font-style: bold;
|
||||
margin: 0;
|
||||
padding: var(--space-s) 0;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Content Container
|
||||
=================================== */
|
||||
|
||||
.utk-foldout > .unity-foldout__content {
|
||||
.utk-foldout .unity-foldout__content {
|
||||
margin-top: var(--space-s);
|
||||
margin-left: var(--space-m);
|
||||
padding: var(--space-m);
|
||||
@@ -73,6 +78,11 @@
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
.utk-foldout .unity-foldout__content {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Collapsed State - Remove content border radius on top
|
||||
=================================== */
|
||||
@@ -82,7 +92,7 @@
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.utk-foldout:checked > .unity-foldout__content {
|
||||
.utk-foldout:checked .unity-foldout__content {
|
||||
margin-top: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
||||
@@ -29,78 +29,29 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Content Text
|
||||
=================================== */
|
||||
|
||||
.utk-scrollview .unity-label {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Vertical Scroller
|
||||
=================================== */
|
||||
|
||||
.utk-scrollview .unity-scroller--vertical {
|
||||
width: 8px;
|
||||
margin-left: var(--space-xs);
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--vertical > .unity-scroller__low-button,
|
||||
.utk-scrollview .unity-scroller--vertical > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--vertical > .unity-scroller__slider {
|
||||
width: 8px;
|
||||
width: 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--vertical .unity-base-slider__tracker {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: 4px;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
width: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-text-disabled);
|
||||
border-width: 0;
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Horizontal Scroller
|
||||
=================================== */
|
||||
|
||||
.utk-scrollview .unity-scroller--horizontal {
|
||||
height: 8px;
|
||||
margin-top: var(--space-xs);
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--horizontal > .unity-scroller__low-button,
|
||||
.utk-scrollview .unity-scroller--horizontal > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--horizontal > .unity-scroller__slider {
|
||||
height: 8px;
|
||||
height: 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--horizontal .unity-base-slider__tracker {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: 4px;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--horizontal .unity-base-slider__dragger {
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-text-disabled);
|
||||
border-width: 0;
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
.utk-scrollview .unity-scroller--horizontal .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.utk-tooltip-label {
|
||||
color: var(--color-text-primary);
|
||||
color: #FFFFFF;
|
||||
font-size: var(--font-size-body2);
|
||||
white-space: normal;
|
||||
-unity-text-align: middle-left;
|
||||
|
||||
@@ -71,19 +71,6 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.accordion-tree-view .unity-scroller--vertical {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.accordion-tree-view .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
background-color: var(--color-scroller-dragger-normal);
|
||||
border-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
.accordion-tree-view .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-scroller-dragger-hover);
|
||||
}
|
||||
|
||||
.accordion-tree-view .unity-tree-view__item {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
@@ -60,19 +60,6 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#main-list-view .unity-scroller--vertical {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
#main-list-view .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
background-color: rgb(80, 80, 82);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#main-list-view .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: rgb(100, 100, 102);
|
||||
}
|
||||
|
||||
.image-list-row {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
=================================== */
|
||||
|
||||
.utk-listview .unity-list-view__item {
|
||||
padding-top: var(--space-s);
|
||||
padding-bottom: var(--space-s);
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: var(--space-m);
|
||||
padding-right: var(--space-m);
|
||||
margin: 0;
|
||||
@@ -40,14 +40,10 @@
|
||||
transition-property: background-color;
|
||||
font-size: var(--font-size-body2);
|
||||
height: auto;
|
||||
min-height: 28px;
|
||||
min-height: 24px;
|
||||
-unity-text-align: middle-left;
|
||||
}
|
||||
|
||||
.utk-listview .unity-collection-view__item--selected {
|
||||
color: var(--color-base-01);
|
||||
}
|
||||
|
||||
/* Unity USS does not support :last-child pseudo-class */
|
||||
/* .utk-listview .unity-list-view__item:last-child {
|
||||
border-bottom-width: 0;
|
||||
@@ -57,10 +53,13 @@
|
||||
background-color: var(--color-btn-hover);
|
||||
}
|
||||
|
||||
/* Unity ListView uses unity-collection-view__item--selected class internally */
|
||||
.utk-listview .unity-collection-view__item--selected,
|
||||
.utk-listview .unity-list-view__item--selected {
|
||||
background-color: var(--color-btn-primary);
|
||||
}
|
||||
|
||||
.utk-listview .unity-collection-view__item--selected:hover,
|
||||
.utk-listview .unity-list-view__item--selected:hover {
|
||||
background-color: var(--color-btn-primary-hover);
|
||||
}
|
||||
@@ -78,9 +77,11 @@
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.utk-listview .unity-collection-view__item--selected .unity-label,
|
||||
.utk-listview .unity-collection-view__item--selected .unity-text-element,
|
||||
.utk-listview .unity-list-view__item--selected .unity-label,
|
||||
.utk-listview .unity-list-view__item--selected .unity-text-element {
|
||||
color: var(--color-base-01) !important;
|
||||
color: var(--color-text-on-primary) !important;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -90,31 +91,6 @@
|
||||
/* Note: Alternating rows must be handled in C# code
|
||||
Unity USS does not support :nth-child() pseudo-class */
|
||||
|
||||
/* ===================================
|
||||
Scrollbar
|
||||
=================================== */
|
||||
|
||||
.utk-listview .unity-scroller--vertical > .unity-scroller__low-button,
|
||||
.utk-listview .unity-scroller--vertical > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.utk-listview .unity-scroller--vertical .unity-base-slider__tracker {
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-listview .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
width: 6px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-text-disabled);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-listview .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Empty State
|
||||
=================================== */
|
||||
|
||||
@@ -46,7 +46,11 @@
|
||||
.utk-multicolumn-listview .unity-multi-column-header__column-title {
|
||||
font-size: var(--font-size-body2);
|
||||
-unity-font-style: bold;
|
||||
color: var(--color-text-primary);
|
||||
color: var(--color-text-primary) !important;
|
||||
}
|
||||
|
||||
.utk-multicolumn-listview .unity-multi-column-header__column .unity-text-element {
|
||||
color: var(--color-text-primary) !important;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -92,7 +96,7 @@
|
||||
=================================== */
|
||||
|
||||
.utk-multicolumn-listview .unity-multi-column-view__cell {
|
||||
padding: var(--space-s) var(--space-m);
|
||||
padding: 0 var(--space-m);
|
||||
border-right-width: 1px;
|
||||
border-right-color: var(--color-border-light);
|
||||
}
|
||||
@@ -105,6 +109,8 @@
|
||||
.utk-multicolumn-listview .unity-multi-column-view__cell > .unity-label {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.utk-multicolumn-listview .unity-list-view__item--selected .unity-multi-column-view__cell > .unity-label {
|
||||
@@ -118,35 +124,6 @@
|
||||
/* Note: Alternating rows must be handled in C# code
|
||||
Unity USS does not support :nth-child() pseudo-class */
|
||||
|
||||
/* ===================================
|
||||
Scrollbar
|
||||
=================================== */
|
||||
|
||||
.utk-multicolumn-listview .unity-scroller--vertical {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.utk-multicolumn-listview .unity-scroller--vertical > .unity-scroller__low-button,
|
||||
.utk-multicolumn-listview .unity-scroller--vertical > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.utk-multicolumn-listview .unity-scroller--vertical .unity-base-slider__tracker {
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-multicolumn-listview .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
width: 6px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-text-disabled);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-multicolumn-listview .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Sort Indicator
|
||||
=================================== */
|
||||
|
||||
@@ -46,7 +46,11 @@
|
||||
.utk-multicolumn-treeview .unity-multi-column-header__column-title {
|
||||
font-size: var(--font-size-body2);
|
||||
-unity-font-style: bold;
|
||||
color: var(--color-text-primary);
|
||||
color: var(--color-text-primary) !important;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-multi-column-header__column .unity-text-element {
|
||||
color: var(--color-text-primary) !important;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -100,10 +104,6 @@
|
||||
transition-property: rotate;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-tree-view__item-toggle:checked {
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-tree-view__item--selected .unity-tree-view__item-toggle {
|
||||
-unity-background-image-tint-color: var(--color-text-on-primary);
|
||||
}
|
||||
@@ -125,9 +125,10 @@
|
||||
=================================== */
|
||||
|
||||
.utk-multicolumn-treeview .unity-multi-column-view__cell {
|
||||
padding: var(--space-s) var(--space-m);
|
||||
padding: 0 var(--space-m);
|
||||
border-right-width: 1px;
|
||||
border-right-color: var(--color-border-light);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* Unity USS does not support :last-child pseudo-class */
|
||||
@@ -144,35 +145,6 @@
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Scrollbar
|
||||
=================================== */
|
||||
|
||||
.utk-multicolumn-treeview .unity-scroller--vertical {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-scroller--vertical > .unity-scroller__low-button,
|
||||
.utk-multicolumn-treeview .unity-scroller--vertical > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-scroller--vertical .unity-base-slider__tracker {
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
width: 6px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-text-disabled);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-multicolumn-treeview .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Sort Indicator
|
||||
=================================== */
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
=================================== */
|
||||
|
||||
.utk-treeview .unity-tree-view__item {
|
||||
padding: var(--space-xs) 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: background-color;
|
||||
@@ -32,10 +32,13 @@
|
||||
background-color: var(--color-btn-hover);
|
||||
}
|
||||
|
||||
/* Unity TreeView uses unity-collection-view__item--selected class internally */
|
||||
.utk-treeview .unity-collection-view__item--selected,
|
||||
.utk-treeview .unity-tree-view__item--selected {
|
||||
background-color: var(--color-btn-primary);
|
||||
}
|
||||
|
||||
.utk-treeview .unity-collection-view__item--selected:hover,
|
||||
.utk-treeview .unity-tree-view__item--selected:hover {
|
||||
background-color: var(--color-btn-primary-hover);
|
||||
}
|
||||
@@ -47,20 +50,30 @@
|
||||
.utk-treeview .unity-tree-view__item-toggle {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: var(--space-xs);
|
||||
-unity-background-image-tint-color: var(--color-text-secondary);
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: rotate;
|
||||
}
|
||||
|
||||
.utk-treeview .unity-tree-view__item-toggle:checked {
|
||||
rotate: 0deg;
|
||||
/* Override #unity-checkmark white color from UTKDefaultStyle.uss */
|
||||
.utk-treeview .unity-tree-view__item-toggle #unity-checkmark {
|
||||
-unity-background-image-tint-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.utk-treeview .unity-tree-view__item-toggle:hover #unity-checkmark {
|
||||
-unity-background-image-tint-color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.utk-treeview .unity-collection-view__item--selected .unity-tree-view__item-toggle,
|
||||
.utk-treeview .unity-tree-view__item--selected .unity-tree-view__item-toggle {
|
||||
-unity-background-image-tint-color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
.utk-treeview .unity-collection-view__item--selected .unity-tree-view__item-toggle #unity-checkmark,
|
||||
.utk-treeview .unity-tree-view__item--selected .unity-tree-view__item-toggle #unity-checkmark {
|
||||
-unity-background-image-tint-color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Indentation
|
||||
=================================== */
|
||||
@@ -88,35 +101,7 @@
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.utk-treeview .unity-collection-view__item--selected .unity-tree-view__item-content > .unity-label,
|
||||
.utk-treeview .unity-tree-view__item--selected .unity-tree-view__item-content > .unity-label {
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Scrollbar
|
||||
=================================== */
|
||||
|
||||
.utk-treeview .unity-scroller--vertical {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.utk-treeview .unity-scroller--vertical > .unity-scroller__low-button,
|
||||
.utk-treeview .unity-scroller--vertical > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.utk-treeview .unity-scroller--vertical .unity-base-slider__tracker {
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-treeview .unity-scroller--vertical .unity-base-slider__dragger {
|
||||
width: 6px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-text-disabled);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.utk-treeview .unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
|
||||
.utk-alert {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 280px;
|
||||
max-width: 400px;
|
||||
padding: var(--space-xl);
|
||||
padding-top: var(--space-m);
|
||||
padding-left: var(--space-xl);
|
||||
padding-right: var(--space-xl);
|
||||
padding-bottom: var(--space-xl);
|
||||
background-color: var(--color-bg-modal);
|
||||
border-radius: var(--radius-l);
|
||||
border-width: var(--border-width);
|
||||
@@ -22,91 +24,33 @@
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Icon Container
|
||||
=================================== */
|
||||
|
||||
.utk-alert__icon-container {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--radius-full);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: var(--space-l);
|
||||
}
|
||||
|
||||
.utk-alert__icon {
|
||||
font-size: 24px;
|
||||
-unity-text-align: middle-center;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Type Variants - Icons
|
||||
=================================== */
|
||||
|
||||
.utk-alert--info .utk-alert__icon-container {
|
||||
background-color: var(--color-state-info);
|
||||
}
|
||||
|
||||
.utk-alert--info .utk-alert__icon {
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
.utk-alert--success .utk-alert__icon-container {
|
||||
background-color: var(--color-state-success);
|
||||
}
|
||||
|
||||
.utk-alert--success .utk-alert__icon {
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
.utk-alert--warning .utk-alert__icon-container {
|
||||
background-color: var(--color-state-warning);
|
||||
}
|
||||
|
||||
.utk-alert--warning .utk-alert__icon {
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
.utk-alert--error .utk-alert__icon-container {
|
||||
background-color: var(--color-state-error);
|
||||
}
|
||||
|
||||
.utk-alert--error .utk-alert__icon {
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
.utk-alert--confirm .utk-alert__icon-container {
|
||||
background-color: var(--color-btn-primary);
|
||||
}
|
||||
|
||||
.utk-alert--confirm .utk-alert__icon {
|
||||
color: var(--color-text-on-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Content
|
||||
=================================== */
|
||||
|
||||
.utk-alert__content {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-l);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Title
|
||||
Title (상단 왼쪽)
|
||||
=================================== */
|
||||
|
||||
.utk-alert__title {
|
||||
font-size: var(--font-size-h3);
|
||||
color: var(--color-text-primary);
|
||||
-unity-font-style: bold;
|
||||
-unity-text-align: middle-center;
|
||||
margin-bottom: var(--space-s);
|
||||
-unity-text-align: middle-left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Message
|
||||
Content (가운데)
|
||||
=================================== */
|
||||
|
||||
.utk-alert__content {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
margin-top: var(--space-l);
|
||||
margin-bottom: var(--space-l);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Message (가운데)
|
||||
=================================== */
|
||||
|
||||
.utk-alert__message {
|
||||
@@ -130,3 +74,27 @@
|
||||
margin-left: var(--space-s);
|
||||
margin-right: var(--space-s);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Type Variants - Title Color
|
||||
=================================== */
|
||||
|
||||
.utk-alert--info .utk-alert__title {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.utk-alert--success .utk-alert__title {
|
||||
color: var(--color-state-success);
|
||||
}
|
||||
|
||||
.utk-alert--warning .utk-alert__title {
|
||||
color: var(--color-state-warning);
|
||||
}
|
||||
|
||||
.utk-alert--error .utk-alert__title {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
.utk-alert--confirm .utk-alert__title {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-l);
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.utk-color-picker__title {
|
||||
|
||||
@@ -187,6 +187,67 @@
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Date Range Selection Styles
|
||||
=================================== */
|
||||
|
||||
.utk-date-picker__day-btn--range-start {
|
||||
background-color: var(--color-calendar-selected);
|
||||
color: var(--color-text-on-primary);
|
||||
-unity-font-style: bold;
|
||||
border-top-left-radius: var(--radius-s);
|
||||
border-bottom-left-radius: var(--radius-s);
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.utk-date-picker__day-btn--range-start:hover {
|
||||
background-color: var(--color-btn-primary-hover);
|
||||
}
|
||||
|
||||
.utk-date-picker__day-btn--range-end {
|
||||
background-color: var(--color-calendar-selected);
|
||||
color: var(--color-text-on-primary);
|
||||
-unity-font-style: bold;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-right-radius: var(--radius-s);
|
||||
border-bottom-right-radius: var(--radius-s);
|
||||
}
|
||||
|
||||
.utk-date-picker__day-btn--range-end:hover {
|
||||
background-color: var(--color-btn-primary-hover);
|
||||
}
|
||||
|
||||
/* 시작일과 종료일이 같은 경우 */
|
||||
.utk-date-picker__day-btn--range-start.utk-date-picker__day-btn--range-end {
|
||||
border-radius: var(--radius-s);
|
||||
}
|
||||
|
||||
.utk-date-picker__day-btn--in-range {
|
||||
background-color: var(--color-calendar-range);
|
||||
color: var(--color-text-primary);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.utk-date-picker__day-btn--in-range:hover {
|
||||
background-color: var(--color-calendar-range-hover);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Range Info Label
|
||||
=================================== */
|
||||
|
||||
.utk-date-picker__range-info {
|
||||
margin-top: var(--space-m);
|
||||
padding: var(--space-s) var(--space-m);
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-secondary);
|
||||
-unity-text-align: middle-center;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Time Picker Row
|
||||
=================================== */
|
||||
|
||||
@@ -95,6 +95,11 @@
|
||||
padding: var(--space-l);
|
||||
}
|
||||
|
||||
.utk-panel__content .unity-label {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.utk-panel--collapsed .utk-panel__content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -20,56 +20,24 @@
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Icon
|
||||
Type Variants - Message Color
|
||||
Toast는 항상 어두운 배경을 사용하므로 밝은 색상 직접 지정
|
||||
=================================== */
|
||||
|
||||
.utk-toast__icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: var(--space-m);
|
||||
font-size: 14px;
|
||||
-unity-text-align: middle-center;
|
||||
border-radius: var(--radius-full);
|
||||
.utk-toast--info .utk-toast__message {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Type Variants
|
||||
=================================== */
|
||||
|
||||
.utk-toast--info {
|
||||
border-left-width: 3px;
|
||||
border-left-color: var(--color-state-info);
|
||||
.utk-toast--success .utk-toast__message {
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.utk-toast--info .utk-toast__icon {
|
||||
color: var(--color-state-info);
|
||||
.utk-toast--warning .utk-toast__message {
|
||||
color: #FFB74D;
|
||||
}
|
||||
|
||||
.utk-toast--success {
|
||||
border-left-width: 3px;
|
||||
border-left-color: var(--color-state-success);
|
||||
}
|
||||
|
||||
.utk-toast--success .utk-toast__icon {
|
||||
color: var(--color-state-success);
|
||||
}
|
||||
|
||||
.utk-toast--warning {
|
||||
border-left-width: 3px;
|
||||
border-left-color: var(--color-state-warning);
|
||||
}
|
||||
|
||||
.utk-toast--warning .utk-toast__icon {
|
||||
color: var(--color-state-warning);
|
||||
}
|
||||
|
||||
.utk-toast--error {
|
||||
border-left-width: 3px;
|
||||
border-left-color: var(--color-state-error);
|
||||
}
|
||||
|
||||
.utk-toast--error .utk-toast__icon {
|
||||
color: var(--color-state-error);
|
||||
.utk-toast--error .utk-toast__message {
|
||||
color: #EF5350;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -79,7 +47,7 @@
|
||||
.utk-toast__message {
|
||||
flex-grow: 1;
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
color: #FFFFFF;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
|
||||
@@ -354,13 +354,17 @@ ListView/TreeView 항목 텍스트 스타일
|
||||
color: var(--color-base-01) !important;
|
||||
}
|
||||
|
||||
.unity-tree-view__item-content {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* TreeView 항목 텍스트 색상 */
|
||||
.unity-tree-view__item .unity-label,
|
||||
.unity-tree-view__item .unity-text-element {
|
||||
color: var(--color-text-primary) !important;
|
||||
-unity-text-align: middle-left;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.unity-tree-view__item--selected .unity-label,
|
||||
@@ -376,12 +380,16 @@ TreeView 항목 스타일
|
||||
|
||||
/* TreeView 펼치기/접기 화살표 토글 - 크기 및 정렬 조정 */
|
||||
.unity-tree-view__item-toggle {
|
||||
margin-right: 4px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
padding: 0;
|
||||
align-self: center;
|
||||
-unity-background-image-tint-color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
@@ -557,6 +565,10 @@ SetupDraggerEvents() 메서드처럼 코드로 MouseEnterEvent/MouseLeaveEvent
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
.unity-scroller--vertical .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-scroller-dragger-hover);
|
||||
}
|
||||
|
||||
/* 상하 화살표 버튼 - 완전히 숨김 처리 */
|
||||
.unity-scroller--vertical .unity-repeat-button {
|
||||
display: none;
|
||||
@@ -566,6 +578,11 @@ SetupDraggerEvents() 메서드처럼 코드로 MouseEnterEvent/MouseLeaveEvent
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.unity-scroller--vertical > .unity-scroller__low-button,
|
||||
.unity-scroller--vertical > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 슬라이더 - 여백 제거 */
|
||||
.unity-scroller--vertical .unity-slider {
|
||||
margin: 0;
|
||||
@@ -614,6 +631,10 @@ SetupDraggerEvents() 메서드처럼 코드로 MouseEnterEvent/MouseLeaveEvent
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
.unity-scroller--horizontal .unity-base-slider__dragger:hover {
|
||||
background-color: var(--color-scroller-dragger-hover);
|
||||
}
|
||||
|
||||
/* 좌우 화살표 버튼 - 완전히 숨김 처리 */
|
||||
.unity-scroller--horizontal .unity-repeat-button {
|
||||
display: none;
|
||||
@@ -623,6 +644,11 @@ SetupDraggerEvents() 메서드처럼 코드로 MouseEnterEvent/MouseLeaveEvent
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.unity-scroller--horizontal > .unity-scroller__low-button,
|
||||
.unity-scroller--horizontal > .unity-scroller__high-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 슬라이더 - 여백 제거 */
|
||||
.unity-scroller--horizontal .unity-slider {
|
||||
margin: 0;
|
||||
|
||||
@@ -161,6 +161,8 @@
|
||||
--color-calendar-saturday: var(--color-blue-02);
|
||||
--color-calendar-today: var(--color-seti-orange);
|
||||
--color-calendar-selected: var(--color-blue-06);
|
||||
--color-calendar-range: rgba(66, 133, 244, 0.25);
|
||||
--color-calendar-range-hover: rgba(66, 133, 244, 0.35);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - State (color-state-*)
|
||||
|
||||
@@ -168,6 +168,8 @@
|
||||
--color-calendar-saturday: var(--color-blue-02);
|
||||
--color-calendar-today: var(--color-orange-02);
|
||||
--color-calendar-selected: var(--color-blue-02);
|
||||
--color-calendar-range: rgba(66, 133, 244, 0.15);
|
||||
--color-calendar-range-hover: rgba(66, 133, 244, 0.25);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - State (color-state-*)
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKTab.uss
|
||||
* Unity Tab 래핑 스타일
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
/* ===================================
|
||||
Base Tab
|
||||
=================================== */
|
||||
|
||||
.utk-tab {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--space-m) var(--space-l);
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-color: transparent;
|
||||
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: background-color, border-color, color;
|
||||
}
|
||||
|
||||
.utk-tab:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.utk-tab--selected {
|
||||
border-bottom-color: var(--color-btn-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Tab Header (Label Container)
|
||||
=================================== */
|
||||
|
||||
.utk-tab > .unity-tab__header {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Tab Header Label
|
||||
=================================== */
|
||||
|
||||
.utk-tab > .unity-tab__header > .unity-tab__header-label {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-secondary);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.utk-tab--selected > .unity-tab__header > .unity-tab__header-label {
|
||||
color: var(--color-btn-primary);
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Tab Content Container
|
||||
=================================== */
|
||||
|
||||
.utk-tab > .unity-tab__content-container {
|
||||
flex-grow: 1;
|
||||
padding: var(--space-m);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Hide Tab Underline (Use Custom)
|
||||
=================================== */
|
||||
|
||||
.utk-tab > .unity-tab__header > .unity-tab__header-underline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
.utk-tab--disabled {
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-tab--disabled:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.utk-tab--disabled > .unity-tab__header > .unity-tab__header-label {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26349aa08d395b241b70ebcbf9b99671
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
@@ -12,6 +12,7 @@
|
||||
.utk-tabview {
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -42,47 +43,57 @@
|
||||
padding: var(--space-l);
|
||||
}
|
||||
|
||||
/* Tab Content inside unity-tab__content-container */
|
||||
.utk-tabview .unity-tab__content-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.utk-tabview .unity-tab__content-container .unity-label {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Tab styling inside TabView
|
||||
Tab Header inside TabView
|
||||
=================================== */
|
||||
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab {
|
||||
padding: var(--space-m) var(--space-l);
|
||||
background-color: transparent;
|
||||
.utk-tabview .unity-tab__header {
|
||||
background-color: transparent !important;
|
||||
border-width: 0;
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-color: transparent;
|
||||
padding: var(--space-s) var(--space-l);
|
||||
margin-right: var(--space-s);
|
||||
transition-duration: var(--anim-fast);
|
||||
transition-property: background-color, border-color;
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
.utk-tabview .unity-tab__header:hover {
|
||||
background-color: var(--color-btn-hover) !important;
|
||||
}
|
||||
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab:checked {
|
||||
border-bottom-color: var(--color-btn-primary);
|
||||
.utk-tabview .unity-tab__header:checked {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Tab Header inside TabView */
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab > .unity-tab__header {
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab > .unity-tab__header > .unity-tab__header-label {
|
||||
.utk-tabview .unity-tab__header-label {
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-secondary);
|
||||
color: var(--color-text-secondary) !important;
|
||||
}
|
||||
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab:checked > .unity-tab__header > .unity-tab__header-label {
|
||||
color: var(--color-btn-primary);
|
||||
.utk-tabview .unity-tab__header:checked .unity-tab__header-label {
|
||||
color: var(--color-btn-primary) !important;
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
|
||||
/* Hide underline (use custom border) */
|
||||
.utk-tabview > .unity-tab-view__header-container > .unity-tab > .unity-tab__header > .unity-tab__header-underline {
|
||||
display: none;
|
||||
/* Tab Underline */
|
||||
.utk-tabview .unity-tab__header-underline {
|
||||
height: 2px;
|
||||
background-color: transparent !important;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.utk-tabview .unity-tab__header:checked > .unity-tab__header-underline {
|
||||
background-color: var(--color-btn-primary) !important;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit.Modal;
|
||||
@@ -105,7 +106,7 @@ namespace UVC.Sample.UIToolkit
|
||||
alphaRow.style.marginBottom = 15;
|
||||
|
||||
_useAlphaToggle = new Toggle("Use Alpha Channel");
|
||||
_useAlphaToggle.value = false;
|
||||
_useAlphaToggle.value = true;
|
||||
_useAlphaToggle.style.color = Color.white;
|
||||
alphaRow.Add(_useAlphaToggle);
|
||||
|
||||
@@ -117,6 +118,12 @@ namespace UVC.Sample.UIToolkit
|
||||
openPickerBtn.style.marginBottom = 10;
|
||||
container.Add(openPickerBtn);
|
||||
|
||||
// Async 버튼
|
||||
var openPickerAsyncBtn = new Button(() => OpenColorPickerAsync().Forget()) { text = "Open Color Picker (Async)" };
|
||||
openPickerAsyncBtn.style.height = 32;
|
||||
openPickerAsyncBtn.style.marginBottom = 10;
|
||||
container.Add(openPickerAsyncBtn);
|
||||
|
||||
// 프리셋 색상 버튼들
|
||||
var presetLabel = new Label("Preset Colors:");
|
||||
presetLabel.style.color = Color.white;
|
||||
@@ -162,6 +169,30 @@ namespace UVC.Sample.UIToolkit
|
||||
_currentPicker.OnColorSelected += OnColorSelected;
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenColorPickerAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
bool useAlpha = _useAlphaToggle?.value ?? true;
|
||||
|
||||
// ShowAsync를 사용하여 색상 선택 대기
|
||||
// OK 클릭 시 선택된 색상 반환, 취소/닫기 시 _currentColor 반환
|
||||
Color selectedColor = await UTKColorPicker.ShowAsync(_root, _currentColor, "Select Color (Async)", useAlpha);
|
||||
|
||||
// 결과 처리
|
||||
_currentColor = selectedColor;
|
||||
if (_colorPreview != null)
|
||||
{
|
||||
_colorPreview.style.backgroundColor = selectedColor;
|
||||
}
|
||||
if (_colorLabel != null)
|
||||
{
|
||||
_colorLabel.text = ColorToHex(selectedColor);
|
||||
}
|
||||
|
||||
Debug.Log($"[Async] Color Result: {ColorToHex(selectedColor)}");
|
||||
}
|
||||
|
||||
private void OnColorChanged(Color color)
|
||||
{
|
||||
// 실시간 미리보기 업데이트
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.Locale;
|
||||
@@ -18,9 +19,12 @@ namespace UVC.Sample.UIToolkit
|
||||
private VisualElement? _root;
|
||||
private Label? _dateLabel;
|
||||
private Label? _dateTimeLabel;
|
||||
private Label? _dateRangeLabel;
|
||||
|
||||
private DateTime _selectedDate = DateTime.Today;
|
||||
private DateTime _selectedDateTime = DateTime.Now;
|
||||
private DateTime _rangeStartDate = DateTime.Today;
|
||||
private DateTime _rangeEndDate = DateTime.Today.AddDays(7);
|
||||
private UTKDatePicker? _currentPicker;
|
||||
|
||||
private void Start()
|
||||
@@ -78,6 +82,44 @@ namespace UVC.Sample.UIToolkit
|
||||
_dateTimeLabel.text = FormatDateTime(_selectedDateTime);
|
||||
container.Add(dateTimeSection.container);
|
||||
|
||||
// Async 섹션
|
||||
var asyncSection = new VisualElement();
|
||||
asyncSection.style.marginBottom = 15;
|
||||
|
||||
var asyncLabel = new Label("Async/Await Mode");
|
||||
asyncLabel.style.color = new Color(0.8f, 0.8f, 0.8f);
|
||||
asyncLabel.style.fontSize = 12;
|
||||
asyncLabel.style.marginBottom = 5;
|
||||
asyncSection.Add(asyncLabel);
|
||||
|
||||
var asyncBtn = new Button(() => OpenDatePickerAsync().Forget()) { text = "Open Date Picker (Async)" };
|
||||
asyncBtn.style.height = 32;
|
||||
asyncSection.Add(asyncBtn);
|
||||
|
||||
container.Add(asyncSection);
|
||||
|
||||
// Date Range 섹션
|
||||
var rangeSection = CreateSection("Date Range Mode", "Open Range Picker", OpenDateRangePicker);
|
||||
_dateRangeLabel = rangeSection.label;
|
||||
_dateRangeLabel.text = FormatDateRange(_rangeStartDate, _rangeEndDate);
|
||||
container.Add(rangeSection.container);
|
||||
|
||||
// Date Range Async 섹션
|
||||
var rangeAsyncSection = new VisualElement();
|
||||
rangeAsyncSection.style.marginBottom = 15;
|
||||
|
||||
var rangeAsyncLabel = new Label("Date Range Async Mode");
|
||||
rangeAsyncLabel.style.color = new Color(0.8f, 0.8f, 0.8f);
|
||||
rangeAsyncLabel.style.fontSize = 12;
|
||||
rangeAsyncLabel.style.marginBottom = 5;
|
||||
rangeAsyncSection.Add(rangeAsyncLabel);
|
||||
|
||||
var rangeAsyncBtn = new Button(() => OpenDateRangePickerAsync().Forget()) { text = "Open Range Picker (Async)" };
|
||||
rangeAsyncBtn.style.height = 32;
|
||||
rangeAsyncSection.Add(rangeAsyncBtn);
|
||||
|
||||
container.Add(rangeAsyncSection);
|
||||
|
||||
// 구분선
|
||||
var separator = new VisualElement();
|
||||
separator.style.height = 1;
|
||||
@@ -185,6 +227,34 @@ namespace UVC.Sample.UIToolkit
|
||||
_currentPicker.OnClosed += OnPickerClosed;
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenDatePickerAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
// ShowAsync를 사용하여 날짜 선택 대기
|
||||
// OK 클릭 시 선택된 날짜 반환, 취소/닫기 시 null 반환
|
||||
DateTime? result = await UTKDatePicker.ShowAsync(
|
||||
_root,
|
||||
_selectedDate,
|
||||
UTKDatePicker.PickerMode.DateOnly,
|
||||
"Select Date (Async)"
|
||||
);
|
||||
|
||||
if (result.HasValue)
|
||||
{
|
||||
_selectedDate = result.Value;
|
||||
if (_dateLabel != null)
|
||||
{
|
||||
_dateLabel.text = FormatDate(result.Value);
|
||||
}
|
||||
Debug.Log($"[Async] Date Result: {FormatDate(result.Value)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Async] Date selection cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDateSelected(DateTime date)
|
||||
{
|
||||
_selectedDate = date;
|
||||
@@ -245,12 +315,75 @@ namespace UVC.Sample.UIToolkit
|
||||
return dateTime.ToString("yyyy-MM-dd HH:mm");
|
||||
}
|
||||
|
||||
private string FormatDateRange(DateTime start, DateTime end)
|
||||
{
|
||||
return $"{start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}";
|
||||
}
|
||||
|
||||
private void OpenDateRangePicker()
|
||||
{
|
||||
if (_root == null || _currentPicker != null) return;
|
||||
|
||||
_currentPicker = UTKDatePicker.ShowRange(
|
||||
_root,
|
||||
_rangeStartDate,
|
||||
_rangeEndDate,
|
||||
false,
|
||||
"Select Date Range"
|
||||
);
|
||||
|
||||
_currentPicker.OnDateRangeSelected += OnDateRangeSelected;
|
||||
_currentPicker.OnClosed += OnPickerClosed;
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenDateRangePickerAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
// ShowRangeAsync를 사용하여 날짜 범위 선택 대기
|
||||
// OK 클릭 시 선택된 범위 반환, 취소/닫기 시 null 반환
|
||||
var result = await UTKDatePicker.ShowRangeAsync(
|
||||
_root,
|
||||
_rangeStartDate,
|
||||
_rangeEndDate,
|
||||
false,
|
||||
"Select Date Range (Async)"
|
||||
);
|
||||
|
||||
if (result.HasValue)
|
||||
{
|
||||
_rangeStartDate = result.Value.Start;
|
||||
_rangeEndDate = result.Value.End;
|
||||
if (_dateRangeLabel != null)
|
||||
{
|
||||
_dateRangeLabel.text = FormatDateRange(result.Value.Start, result.Value.End);
|
||||
}
|
||||
Debug.Log($"[Async] Range Result: {FormatDateRange(result.Value.Start, result.Value.End)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Async] Date range selection cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDateRangeSelected(DateTime start, DateTime end)
|
||||
{
|
||||
_rangeStartDate = start;
|
||||
_rangeEndDate = end;
|
||||
if (_dateRangeLabel != null)
|
||||
{
|
||||
_dateRangeLabel.text = FormatDateRange(start, end);
|
||||
}
|
||||
Debug.Log($"Date Range Selected: {FormatDateRange(start, end)}");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_currentPicker != null)
|
||||
{
|
||||
_currentPicker.OnDateSelected -= OnDateSelected;
|
||||
_currentPicker.OnDateSelected -= OnDateTimeSelected;
|
||||
_currentPicker.OnDateRangeSelected -= OnDateRangeSelected;
|
||||
_currentPicker.OnClosed -= OnPickerClosed;
|
||||
_currentPicker.Dispose();
|
||||
_currentPicker = null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit;
|
||||
@@ -787,6 +788,21 @@ public class UTKStyleGuideSample : MonoBehaviour
|
||||
}),
|
||||
}),
|
||||
new TreeViewItemData<string>(9, "Parent 3"),
|
||||
new TreeViewItemData<string>(10, "Parent 4", new List<TreeViewItemData<string>>
|
||||
{
|
||||
new TreeViewItemData<string>(11, "Child 4-1"),
|
||||
new TreeViewItemData<string>(12, "Child 4-2"),
|
||||
}),
|
||||
new TreeViewItemData<string>(13, "Parent 5", new List<TreeViewItemData<string>>
|
||||
{
|
||||
new TreeViewItemData<string>(14, "Child 5-1"),
|
||||
new TreeViewItemData<string>(15, "Child 5-2", new List<TreeViewItemData<string>>
|
||||
{
|
||||
new TreeViewItemData<string>(16, "Grandchild 5-2-1"),
|
||||
new TreeViewItemData<string>(17, "Grandchild 5-2-2"),
|
||||
}),
|
||||
}),
|
||||
new TreeViewItemData<string>(18, "Parent 6"),
|
||||
};
|
||||
|
||||
treeView.SetRootItems(treeItems);
|
||||
@@ -794,31 +810,138 @@ public class UTKStyleGuideSample : MonoBehaviour
|
||||
container.Add(treeView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MultiColumnListView 샘플 데이터 클래스
|
||||
/// </summary>
|
||||
private class SampleListItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string Status { get; set; } = "";
|
||||
public int Progress { get; set; }
|
||||
}
|
||||
|
||||
private void CreateMultiColumnListViewPreview(VisualElement container)
|
||||
{
|
||||
AddDescription(container, "멀티 컬럼 리스트 뷰 (정렬 가능한 테이블)");
|
||||
|
||||
var placeholder = new Label("MultiColumnListView requires Columns and data binding setup.");
|
||||
placeholder.style.color = new Color(0.6f, 0.6f, 0.6f);
|
||||
container.Add(placeholder);
|
||||
|
||||
var listView = new UTKMultiColumnListView();
|
||||
listView.style.height = 200;
|
||||
listView.style.width = 400;
|
||||
listView.style.width = 450;
|
||||
|
||||
// 컬럼 정의
|
||||
listView.columns.Add(new Column { name = "id", title = "ID", width = 50 });
|
||||
listView.columns.Add(new Column { name = "name", title = "Name", width = 150, stretchable = true });
|
||||
listView.columns.Add(new Column { name = "status", title = "Status", width = 100 });
|
||||
listView.columns.Add(new Column { name = "progress", title = "Progress", width = 80 });
|
||||
|
||||
// 샘플 데이터
|
||||
var items = new List<SampleListItem>
|
||||
{
|
||||
new() { Id = 1, Name = "Task Alpha", Status = "Active", Progress = 75 },
|
||||
new() { Id = 2, Name = "Task Beta", Status = "Pending", Progress = 30 },
|
||||
new() { Id = 3, Name = "Task Gamma", Status = "Completed", Progress = 100 },
|
||||
new() { Id = 4, Name = "Task Delta", Status = "Active", Progress = 50 },
|
||||
new() { Id = 5, Name = "Task Epsilon", Status = "Cancelled", Progress = 0 },
|
||||
new() { Id = 6, Name = "Task Zeta", Status = "Active", Progress = 90 },
|
||||
new() { Id = 7, Name = "Task Eta", Status = "Pending", Progress = 15 },
|
||||
new() { Id = 8, Name = "Task Theta", Status = "Completed", Progress = 100 },
|
||||
};
|
||||
|
||||
listView.itemsSource = items;
|
||||
listView.fixedItemHeight = 24;
|
||||
|
||||
// 셀 생성 및 바인딩
|
||||
listView.columns["id"].makeCell = () => new Label();
|
||||
listView.columns["id"].bindCell = (e, i) => ((Label)e).text = items[i].Id.ToString();
|
||||
|
||||
listView.columns["name"].makeCell = () => new Label();
|
||||
listView.columns["name"].bindCell = (e, i) => ((Label)e).text = items[i].Name;
|
||||
|
||||
listView.columns["status"].makeCell = () => new Label();
|
||||
listView.columns["status"].bindCell = (e, i) => ((Label)e).text = items[i].Status;
|
||||
|
||||
listView.columns["progress"].makeCell = () => new Label();
|
||||
listView.columns["progress"].bindCell = (e, i) => ((Label)e).text = $"{items[i].Progress}%";
|
||||
|
||||
container.Add(listView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MultiColumnTreeView 샘플 데이터 클래스
|
||||
/// </summary>
|
||||
private class SampleTreeItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string Type { get; set; } = "";
|
||||
public string Size { get; set; } = "";
|
||||
}
|
||||
|
||||
private void CreateMultiColumnTreeViewPreview(VisualElement container)
|
||||
{
|
||||
AddDescription(container, "멀티 컬럼 트리 뷰 (계층적 테이블)");
|
||||
|
||||
var placeholder = new Label("MultiColumnTreeView requires Columns and TreeViewItemData setup.");
|
||||
placeholder.style.color = new Color(0.6f, 0.6f, 0.6f);
|
||||
container.Add(placeholder);
|
||||
|
||||
var treeView = new UTKMultiColumnTreeView();
|
||||
treeView.style.height = 200;
|
||||
treeView.style.width = 400;
|
||||
treeView.style.height = 220;
|
||||
treeView.style.width = 450;
|
||||
|
||||
// 컬럼 정의
|
||||
treeView.columns.Add(new Column { name = "name", title = "Name", width = 200, stretchable = true });
|
||||
treeView.columns.Add(new Column { name = "type", title = "Type", width = 100 });
|
||||
treeView.columns.Add(new Column { name = "size", title = "Size", width = 80 });
|
||||
|
||||
// 샘플 데이터 (계층 구조)
|
||||
var rootItems = new List<TreeViewItemData<SampleTreeItem>>
|
||||
{
|
||||
new(1, new SampleTreeItem { Id = 1, Name = "Documents", Type = "Folder", Size = "2.5 GB" },
|
||||
new List<TreeViewItemData<SampleTreeItem>>
|
||||
{
|
||||
new(11, new SampleTreeItem { Id = 11, Name = "Reports", Type = "Folder", Size = "500 MB" },
|
||||
new List<TreeViewItemData<SampleTreeItem>>
|
||||
{
|
||||
new(111, new SampleTreeItem { Id = 111, Name = "Q1_Report.pdf", Type = "PDF", Size = "2.3 MB" }),
|
||||
new(112, new SampleTreeItem { Id = 112, Name = "Q2_Report.pdf", Type = "PDF", Size = "1.8 MB" }),
|
||||
}),
|
||||
new(12, new SampleTreeItem { Id = 12, Name = "Images", Type = "Folder", Size = "1.2 GB" }),
|
||||
}),
|
||||
new(2, new SampleTreeItem { Id = 2, Name = "Projects", Type = "Folder", Size = "5.0 GB" },
|
||||
new List<TreeViewItemData<SampleTreeItem>>
|
||||
{
|
||||
new(21, new SampleTreeItem { Id = 21, Name = "ProjectA", Type = "Folder", Size = "2.0 GB" }),
|
||||
new(22, new SampleTreeItem { Id = 22, Name = "ProjectB", Type = "Folder", Size = "3.0 GB" }),
|
||||
}),
|
||||
new(3, new SampleTreeItem { Id = 3, Name = "Config.json", Type = "JSON", Size = "4 KB" }),
|
||||
};
|
||||
|
||||
treeView.SetRootItems(rootItems);
|
||||
treeView.fixedItemHeight = 22;
|
||||
|
||||
// 셀 생성 및 바인딩
|
||||
treeView.columns["name"].makeCell = () => new Label();
|
||||
treeView.columns["name"].bindCell = (e, i) =>
|
||||
{
|
||||
var item = treeView.GetItemDataForIndex<SampleTreeItem>(i);
|
||||
((Label)e).text = item.Name;
|
||||
};
|
||||
|
||||
treeView.columns["type"].makeCell = () => new Label();
|
||||
treeView.columns["type"].bindCell = (e, i) =>
|
||||
{
|
||||
var item = treeView.GetItemDataForIndex<SampleTreeItem>(i);
|
||||
((Label)e).text = item.Type;
|
||||
};
|
||||
|
||||
treeView.columns["size"].makeCell = () => new Label();
|
||||
treeView.columns["size"].bindCell = (e, i) =>
|
||||
{
|
||||
var item = treeView.GetItemDataForIndex<SampleTreeItem>(i);
|
||||
((Label)e).text = item.Size;
|
||||
};
|
||||
|
||||
// 기본 확장
|
||||
treeView.ExpandAll();
|
||||
|
||||
container.Add(treeView);
|
||||
}
|
||||
|
||||
@@ -840,16 +963,50 @@ public class UTKStyleGuideSample : MonoBehaviour
|
||||
{
|
||||
AddDescription(container, "스크롤 뷰 컴포넌트");
|
||||
|
||||
var scrollView = new UTKScrollView();
|
||||
scrollView.style.height = 150;
|
||||
scrollView.style.width = 300;
|
||||
var row = CreateRow(container, "ScrollViews");
|
||||
|
||||
// 세로 스크롤
|
||||
var verticalScroll = new UTKScrollView();
|
||||
verticalScroll.style.height = 150;
|
||||
verticalScroll.style.width = 200;
|
||||
|
||||
for (int i = 1; i <= 15; i++)
|
||||
{
|
||||
scrollView.Add(new Label($"Scrollable Item {i}"));
|
||||
verticalScroll.Add(new Label($"Vertical Item {i}"));
|
||||
}
|
||||
row.Add(verticalScroll);
|
||||
|
||||
container.Add(scrollView);
|
||||
// 가로 스크롤
|
||||
var horizontalScroll = new UTKScrollView();
|
||||
horizontalScroll.mode = ScrollViewMode.Horizontal;
|
||||
horizontalScroll.style.height = 150;
|
||||
horizontalScroll.style.width = 200;
|
||||
|
||||
var horizontalContent = new VisualElement();
|
||||
horizontalContent.style.flexDirection = FlexDirection.Row;
|
||||
for (int i = 1; i <= 15; i++)
|
||||
{
|
||||
var item = new Label($"H-Item {i}");
|
||||
item.style.minWidth = 80;
|
||||
item.style.marginRight = 8;
|
||||
horizontalContent.Add(item);
|
||||
}
|
||||
horizontalScroll.Add(horizontalContent);
|
||||
row.Add(horizontalScroll);
|
||||
|
||||
// 양방향 스크롤
|
||||
var bothScroll = new UTKScrollView();
|
||||
bothScroll.mode = ScrollViewMode.VerticalAndHorizontal;
|
||||
bothScroll.style.height = 150;
|
||||
bothScroll.style.width = 200;
|
||||
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
var item = new Label($"This is a very long scrollable item number {i} that requires horizontal scrolling");
|
||||
item.style.whiteSpace = WhiteSpace.NoWrap;
|
||||
bothScroll.Add(item);
|
||||
}
|
||||
row.Add(bothScroll);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -909,23 +1066,102 @@ public class UTKStyleGuideSample : MonoBehaviour
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
AddDescription(container, "알림 다이얼로그");
|
||||
AddDescription(container, "알림 다이얼로그 (async/await 지원)");
|
||||
|
||||
var row = CreateRow(container, "Alerts");
|
||||
// Alert Types (Async)
|
||||
var row1 = CreateRow(container, "Alert Types (Async)");
|
||||
|
||||
var infoBtn = new UTKButton("Show Info", "", UTKButton.ButtonVariant.Primary);
|
||||
infoBtn.OnClicked += () => UTKAlert.ShowInfo(_root, "Information", "This is an info message.");
|
||||
row.Add(infoBtn);
|
||||
UTKAlert.SetRoot(_root);
|
||||
|
||||
var warnBtn = new UTKButton("Show Warning", "", UTKButton.ButtonVariant.Normal);
|
||||
warnBtn.OnClicked += () => UTKAlert.ShowWarning(_root, "Warning", "This is a warning message.");
|
||||
row.Add(warnBtn);
|
||||
var infoBtn = new UTKButton("Info", "", UTKButton.ButtonVariant.Primary);
|
||||
infoBtn.OnClicked += () => ShowInfoAlertAsync().Forget();
|
||||
row1.Add(infoBtn);
|
||||
|
||||
var confirmBtn = new UTKButton("Show Confirm", "", UTKButton.ButtonVariant.Normal);
|
||||
confirmBtn.OnClicked += () => UTKAlert.ShowConfirm(_root, "Confirm", "Are you sure?",
|
||||
() => Debug.Log("Confirmed!"),
|
||||
() => Debug.Log("Cancelled!"));
|
||||
row.Add(confirmBtn);
|
||||
var successBtn = new UTKButton("Success", "", UTKButton.ButtonVariant.Normal);
|
||||
successBtn.OnClicked += () => ShowSuccessAlertAsync().Forget();
|
||||
row1.Add(successBtn);
|
||||
|
||||
var warnBtn = new UTKButton("Warning", "", UTKButton.ButtonVariant.Normal);
|
||||
warnBtn.OnClicked += () => ShowWarningAlertAsync().Forget();
|
||||
row1.Add(warnBtn);
|
||||
|
||||
var errorBtn = new UTKButton("Error", "", UTKButton.ButtonVariant.Danger);
|
||||
errorBtn.OnClicked += () => ShowErrorAlertAsync().Forget();
|
||||
row1.Add(errorBtn);
|
||||
|
||||
// Confirm Dialog (Async)
|
||||
var row2 = CreateRow(container, "Confirm Dialog (Async)");
|
||||
|
||||
var confirmBtn = new UTKButton("Confirm", "", UTKButton.ButtonVariant.Normal);
|
||||
confirmBtn.OnClicked += () => ShowConfirmAlertAsync().Forget();
|
||||
row2.Add(confirmBtn);
|
||||
|
||||
var confirmCustomBtn = new UTKButton("Custom Labels", "", UTKButton.ButtonVariant.Normal);
|
||||
confirmCustomBtn.OnClicked += () => ShowConfirmCustomLabelsAsync().Forget();
|
||||
row2.Add(confirmCustomBtn);
|
||||
|
||||
// Callback Style (Non-Async)
|
||||
var row3 = CreateRow(container, "Callback Style");
|
||||
|
||||
var callbackBtn = new UTKButton("With Callback", "", UTKButton.ButtonVariant.OutlineNormal);
|
||||
callbackBtn.OnClicked += () =>
|
||||
{
|
||||
UTKAlert.ShowInfo(_root, "Callback Style", "This uses callback instead of async.",
|
||||
onClose: () => Debug.Log("Alert closed via callback"));
|
||||
};
|
||||
row3.Add(callbackBtn);
|
||||
|
||||
var confirmCallbackBtn = new UTKButton("Confirm Callback", "", UTKButton.ButtonVariant.OutlineNormal);
|
||||
confirmCallbackBtn.OnClicked += () =>
|
||||
{
|
||||
UTKAlert.ShowConfirm(_root, "Confirm", "Do you want to proceed?",
|
||||
onConfirm: () => Debug.Log("Confirmed via callback!"),
|
||||
onCancel: () => Debug.Log("Cancelled via callback!"));
|
||||
};
|
||||
row3.Add(confirmCallbackBtn);
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowInfoAlertAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
await UTKAlert.ShowInfoAsync("Information", "This is an info message.");
|
||||
Debug.Log("Info alert closed");
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowSuccessAlertAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
await UTKAlert.ShowSuccessAsync("Success", "Operation completed successfully!", closeOnBlockerClick: true);
|
||||
Debug.Log("Success alert closed");
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowWarningAlertAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
await UTKAlert.ShowWarningAsync("Warning", "This is a warning message.");
|
||||
Debug.Log("Warning alert closed");
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowErrorAlertAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
await UTKAlert.ShowErrorAsync("Error", "An error has occurred!");
|
||||
Debug.Log("Error alert closed");
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowConfirmAlertAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
bool result = await UTKAlert.ShowConfirmAsync("Confirm", "Are you sure?");
|
||||
Debug.Log(result ? "Confirmed!" : "Cancelled!");
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowConfirmCustomLabelsAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
bool result = await UTKAlert.ShowConfirmAsync("Delete Item", "Are you sure you want to delete this item?",
|
||||
confirmLabel: "Delete", cancelLabel: "Keep");
|
||||
Debug.Log(result ? "Item deleted!" : "Item kept!");
|
||||
}
|
||||
|
||||
private void CreateToastPreview(VisualElement container)
|
||||
@@ -936,16 +1172,22 @@ public class UTKStyleGuideSample : MonoBehaviour
|
||||
|
||||
var row = CreateRow(container, "Toasts");
|
||||
|
||||
UTKToast.SetRoot(_root);
|
||||
|
||||
var infoBtn = new UTKButton("Info Toast", "", UTKButton.ButtonVariant.Primary);
|
||||
infoBtn.OnClicked += () => UTKToast.ShowInfo(_root, "This is an info toast!", 3000);
|
||||
infoBtn.OnClicked += () => UTKToast.Show("This is an info toast!This is an info toast!This is an info toast!This is an info toast!This is an info toast!");
|
||||
row.Add(infoBtn);
|
||||
|
||||
var successBtn = new UTKButton("Success Toast", "", UTKButton.ButtonVariant.Normal);
|
||||
successBtn.OnClicked += () => UTKToast.ShowSuccess(_root, "Operation successful!", 3000);
|
||||
successBtn.OnClicked += () => UTKToast.ShowSuccess("Operation successful!");
|
||||
row.Add(successBtn);
|
||||
|
||||
var warningBtn = new UTKButton("Warning Toast", "", UTKButton.ButtonVariant.Normal);
|
||||
warningBtn.OnClicked += () => UTKToast.ShowWarning("Warning: Check your input!");
|
||||
row.Add(warningBtn);
|
||||
|
||||
var errorBtn = new UTKButton("Error Toast", "", UTKButton.ButtonVariant.Danger);
|
||||
errorBtn.OnClicked += () => UTKToast.ShowError(_root, "An error occurred!", 3000);
|
||||
errorBtn.OnClicked += () => UTKToast.ShowError("An error occurred!");
|
||||
row.Add(errorBtn);
|
||||
}
|
||||
|
||||
@@ -968,37 +1210,227 @@ public class UTKStyleGuideSample : MonoBehaviour
|
||||
|
||||
#region Picker Previews
|
||||
|
||||
private Color _selectedColor = Color.white;
|
||||
private VisualElement? _colorPreviewBox;
|
||||
private Label? _colorHexLabel;
|
||||
|
||||
private void CreateColorPickerPreview(VisualElement container)
|
||||
{
|
||||
AddDescription(container, "색상 선택 컴포넌트");
|
||||
if (_root == null) return;
|
||||
|
||||
var colorLabel = new Label("Selected: #FFFFFFFF");
|
||||
container.Add(colorLabel);
|
||||
AddDescription(container, "색상 선택 컴포넌트 (버튼 클릭으로 모달 표시)");
|
||||
|
||||
var colorPicker = new UTKColorPicker();
|
||||
colorPicker.style.position = Position.Relative;
|
||||
colorPicker.SetColor(Color.white);
|
||||
colorPicker.OnColorChanged += (color) =>
|
||||
{
|
||||
colorLabel.text = $"Selected: #{ColorUtility.ToHtmlStringRGBA(color)}";
|
||||
};
|
||||
container.Add(colorPicker);
|
||||
// 현재 색상 미리보기
|
||||
var previewRow = CreateRow(container, "Current Color");
|
||||
|
||||
_colorPreviewBox = new VisualElement();
|
||||
_colorPreviewBox.style.width = 60;
|
||||
_colorPreviewBox.style.height = 30;
|
||||
_colorPreviewBox.style.backgroundColor = _selectedColor;
|
||||
_colorPreviewBox.style.borderTopLeftRadius = 4;
|
||||
_colorPreviewBox.style.borderTopRightRadius = 4;
|
||||
_colorPreviewBox.style.borderBottomLeftRadius = 4;
|
||||
_colorPreviewBox.style.borderBottomRightRadius = 4;
|
||||
_colorPreviewBox.style.borderTopWidth = 1;
|
||||
_colorPreviewBox.style.borderBottomWidth = 1;
|
||||
_colorPreviewBox.style.borderLeftWidth = 1;
|
||||
_colorPreviewBox.style.borderRightWidth = 1;
|
||||
_colorPreviewBox.style.borderTopColor = new Color(0.4f, 0.4f, 0.4f);
|
||||
_colorPreviewBox.style.borderBottomColor = new Color(0.4f, 0.4f, 0.4f);
|
||||
_colorPreviewBox.style.borderLeftColor = new Color(0.4f, 0.4f, 0.4f);
|
||||
_colorPreviewBox.style.borderRightColor = new Color(0.4f, 0.4f, 0.4f);
|
||||
previewRow.Add(_colorPreviewBox);
|
||||
|
||||
_colorHexLabel = new Label($"#{ColorUtility.ToHtmlStringRGBA(_selectedColor)}");
|
||||
_colorHexLabel.style.marginLeft = 10;
|
||||
previewRow.Add(_colorHexLabel);
|
||||
|
||||
// 버튼들
|
||||
var row1 = CreateRow(container, "With Alpha");
|
||||
var withAlphaBtn = new UTKButton("Open Color Picker (Alpha)", "", UTKButton.ButtonVariant.Primary);
|
||||
withAlphaBtn.OnClicked += () => OpenColorPicker(true);
|
||||
row1.Add(withAlphaBtn);
|
||||
|
||||
var row2 = CreateRow(container, "Without Alpha");
|
||||
var withoutAlphaBtn = new UTKButton("Open Color Picker (No Alpha)", "", UTKButton.ButtonVariant.Normal);
|
||||
withoutAlphaBtn.OnClicked += () => OpenColorPicker(false);
|
||||
row2.Add(withoutAlphaBtn);
|
||||
|
||||
// Async 버튼
|
||||
var row3 = CreateRow(container, "Async/Await");
|
||||
var asyncBtn = new UTKButton("Open Color Picker (Async)", "", UTKButton.ButtonVariant.Normal);
|
||||
asyncBtn.OnClicked += () => OpenColorPickerAsync().Forget();
|
||||
row3.Add(asyncBtn);
|
||||
}
|
||||
|
||||
private void OpenColorPicker(bool useAlpha)
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
var picker = UTKColorPicker.Show(_root, _selectedColor, "Select Color", useAlpha);
|
||||
picker.OnColorChanged += (color) =>
|
||||
{
|
||||
// 실시간 미리보기 업데이트
|
||||
if (_colorPreviewBox != null)
|
||||
_colorPreviewBox.style.backgroundColor = color;
|
||||
if (_colorHexLabel != null)
|
||||
_colorHexLabel.text = useAlpha
|
||||
? $"#{ColorUtility.ToHtmlStringRGBA(color)}"
|
||||
: $"#{ColorUtility.ToHtmlStringRGB(color)}";
|
||||
};
|
||||
picker.OnColorSelected += (color) =>
|
||||
{
|
||||
_selectedColor = color;
|
||||
Debug.Log($"Color Selected: #{(useAlpha ? ColorUtility.ToHtmlStringRGBA(color) : ColorUtility.ToHtmlStringRGB(color))}");
|
||||
};
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenColorPickerAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
// ShowAsync를 사용하여 색상 선택 대기
|
||||
// OK 클릭 시 선택된 색상 반환, 취소/닫기 시 _selectedColor 반환
|
||||
Color result = await UTKColorPicker.ShowAsync(_root, _selectedColor, "Select Color (Async)", useAlpha: true);
|
||||
|
||||
// 결과 처리
|
||||
_selectedColor = result;
|
||||
if (_colorPreviewBox != null)
|
||||
_colorPreviewBox.style.backgroundColor = result;
|
||||
if (_colorHexLabel != null)
|
||||
_colorHexLabel.text = $"#{ColorUtility.ToHtmlStringRGBA(result)}";
|
||||
|
||||
Debug.Log($"[Async] Color Result: #{ColorUtility.ToHtmlStringRGBA(result)}");
|
||||
}
|
||||
|
||||
private DateTime _selectedDate = DateTime.Today;
|
||||
private DateTime _rangeStartDate = DateTime.Today;
|
||||
private DateTime _rangeEndDate = DateTime.Today.AddDays(7);
|
||||
private Label? _dateLabel;
|
||||
private Label? _rangeDateLabel;
|
||||
|
||||
private void CreateDatePickerPreview(VisualElement container)
|
||||
{
|
||||
AddDescription(container, "날짜 선택 컴포넌트");
|
||||
if (_root == null) return;
|
||||
|
||||
var dateLabel = new Label($"Selected: {DateTime.Now:yyyy-MM-dd}");
|
||||
container.Add(dateLabel);
|
||||
AddDescription(container, "날짜 선택 컴포넌트 (버튼 클릭으로 모달 표시)");
|
||||
|
||||
var datePicker = new UTKDatePicker();
|
||||
datePicker.style.position = Position.Relative;
|
||||
datePicker.OnDateSelected += (date) =>
|
||||
// 현재 선택된 날짜 표시
|
||||
var previewRow = CreateRow(container, "Current Date");
|
||||
_dateLabel = new Label($"Selected: {_selectedDate:yyyy-MM-dd}");
|
||||
previewRow.Add(_dateLabel);
|
||||
|
||||
// 날짜만 선택 버튼
|
||||
var row1 = CreateRow(container, "Date Only");
|
||||
var dateOnlyBtn = new UTKButton("Open Date Picker", "", UTKButton.ButtonVariant.Primary);
|
||||
dateOnlyBtn.OnClicked += () => OpenDatePicker(UTKDatePicker.PickerMode.DateOnly);
|
||||
row1.Add(dateOnlyBtn);
|
||||
|
||||
// 날짜 + 시간 선택 버튼
|
||||
var row2 = CreateRow(container, "Date & Time");
|
||||
var dateTimeBtn = new UTKButton("Open DateTime Picker", "", UTKButton.ButtonVariant.Normal);
|
||||
dateTimeBtn.OnClicked += () => OpenDatePicker(UTKDatePicker.PickerMode.DateAndTime);
|
||||
row2.Add(dateTimeBtn);
|
||||
|
||||
// Async 버튼
|
||||
var row3 = CreateRow(container, "Async/Await");
|
||||
var asyncBtn = new UTKButton("Open Date Picker (Async)", "", UTKButton.ButtonVariant.Normal);
|
||||
asyncBtn.OnClicked += () => OpenDatePickerAsync().Forget();
|
||||
row3.Add(asyncBtn);
|
||||
|
||||
// 날짜 범위 표시
|
||||
var rangePreviewRow = CreateRow(container, "Date Range");
|
||||
_rangeDateLabel = new Label($"Range: {_rangeStartDate:yyyy-MM-dd} ~ {_rangeEndDate:yyyy-MM-dd}");
|
||||
rangePreviewRow.Add(_rangeDateLabel);
|
||||
|
||||
// 날짜 범위 선택 버튼
|
||||
var row4 = CreateRow(container, "Date Range");
|
||||
var rangeBtn = new UTKButton("Open Range Picker", "", UTKButton.ButtonVariant.Normal);
|
||||
rangeBtn.OnClicked += OpenDateRangePicker;
|
||||
row4.Add(rangeBtn);
|
||||
|
||||
// 날짜 범위 Async 버튼
|
||||
var row5 = CreateRow(container, "Range Async");
|
||||
var rangeAsyncBtn = new UTKButton("Open Range Picker (Async)", "", UTKButton.ButtonVariant.Normal);
|
||||
rangeAsyncBtn.OnClicked += () => OpenDateRangePickerAsync().Forget();
|
||||
row5.Add(rangeAsyncBtn);
|
||||
}
|
||||
|
||||
private void OpenDatePicker(UTKDatePicker.PickerMode mode)
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
string title = mode == UTKDatePicker.PickerMode.DateOnly ? "Select Date" : "Select Date & Time";
|
||||
var picker = UTKDatePicker.Show(_root, _selectedDate, mode, title);
|
||||
picker.OnDateSelected += (date) =>
|
||||
{
|
||||
dateLabel.text = $"Selected: {date:yyyy-MM-dd HH:mm}";
|
||||
_selectedDate = date;
|
||||
if (_dateLabel != null)
|
||||
{
|
||||
_dateLabel.text = mode == UTKDatePicker.PickerMode.DateOnly
|
||||
? $"Selected: {date:yyyy-MM-dd}"
|
||||
: $"Selected: {date:yyyy-MM-dd HH:mm}";
|
||||
}
|
||||
Debug.Log($"Date Selected: {date:yyyy-MM-dd HH:mm}");
|
||||
};
|
||||
container.Add(datePicker);
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenDatePickerAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
// ShowAsync를 사용하여 날짜 선택 대기
|
||||
// OK 클릭 시 선택된 날짜 반환, 취소/닫기 시 null 반환
|
||||
DateTime? result = await UTKDatePicker.ShowAsync(_root, _selectedDate, UTKDatePicker.PickerMode.DateOnly, "Select Date (Async)");
|
||||
|
||||
if (result.HasValue)
|
||||
{
|
||||
_selectedDate = result.Value;
|
||||
if (_dateLabel != null)
|
||||
_dateLabel.text = $"Selected: {result.Value:yyyy-MM-dd}";
|
||||
Debug.Log($"[Async] Date Result: {result.Value:yyyy-MM-dd}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Async] Date selection cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenDateRangePicker()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
var picker = UTKDatePicker.ShowRange(_root, _rangeStartDate, _rangeEndDate, false, "Select Date Range");
|
||||
picker.OnDateRangeSelected += (start, end) =>
|
||||
{
|
||||
_rangeStartDate = start;
|
||||
_rangeEndDate = end;
|
||||
if (_rangeDateLabel != null)
|
||||
_rangeDateLabel.text = $"Range: {start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}";
|
||||
Debug.Log($"Date Range Selected: {start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}");
|
||||
};
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenDateRangePickerAsync()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
// ShowRangeAsync를 사용하여 날짜 범위 선택 대기
|
||||
// OK 클릭 시 선택된 범위 반환, 취소/닫기 시 null 반환
|
||||
var result = await UTKDatePicker.ShowRangeAsync(_root, _rangeStartDate, _rangeEndDate, false, "Select Date Range (Async)");
|
||||
|
||||
if (result.HasValue)
|
||||
{
|
||||
_rangeStartDate = result.Value.Start;
|
||||
_rangeEndDate = result.Value.End;
|
||||
if (_rangeDateLabel != null)
|
||||
_rangeDateLabel.text = $"Range: {result.Value.Start:yyyy-MM-dd} ~ {result.Value.End:yyyy-MM-dd}";
|
||||
Debug.Log($"[Async] Range Result: {result.Value.Start:yyyy-MM-dd} ~ {result.Value.End:yyyy-MM-dd}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Async] Date range selection cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -181,6 +181,7 @@ namespace UVC.UIToolkit
|
||||
// Actions
|
||||
_actions = new VisualElement { name = "actions" };
|
||||
_actions.AddToClassList("utk-card__actions");
|
||||
_actions.style.display = DisplayStyle.None; // 기본적으로 숨김
|
||||
hierarchy.Add(_actions);
|
||||
|
||||
UpdateVariant();
|
||||
@@ -281,6 +282,13 @@ namespace UVC.UIToolkit
|
||||
public void AddAction(VisualElement element)
|
||||
{
|
||||
_actions?.Add(element);
|
||||
UpdateActionsVisibility();
|
||||
}
|
||||
|
||||
private void UpdateActionsVisibility()
|
||||
{
|
||||
if (_actions == null) return;
|
||||
_actions.style.display = _actions.childCount > 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -159,6 +159,7 @@ namespace UVC.UIToolkit.Common
|
||||
|
||||
_tooltipLabel.text = displayText;
|
||||
_tooltipContainer.style.display = DisplayStyle.Flex;
|
||||
_tooltipContainer.BringToFront();
|
||||
_isVisible = true;
|
||||
|
||||
// 다음 프레임에 위치 조정 (레이아웃 계산 후)
|
||||
@@ -217,11 +218,18 @@ namespace UVC.UIToolkit.Common
|
||||
_tooltipRegistry[element] = tooltip;
|
||||
|
||||
// 이벤트 콜백 생성 및 등록
|
||||
// 참고: evt.position은 로컬 좌표이므로, 패널 기준 좌표로 변환 필요
|
||||
EventCallback<PointerEnterEvent> enterCallback = evt =>
|
||||
{
|
||||
if (_tooltipRegistry.TryGetValue(element, out var text))
|
||||
{
|
||||
ShowDelayed(text, evt.position).Forget();
|
||||
// 로컬 좌표를 root 좌표로 변환
|
||||
var rootPosition = element.LocalToWorld(evt.localPosition);
|
||||
if (_root != null)
|
||||
{
|
||||
rootPosition = _root.WorldToLocal(rootPosition);
|
||||
}
|
||||
ShowDelayed(text, rootPosition).Forget();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -231,7 +239,13 @@ namespace UVC.UIToolkit.Common
|
||||
{
|
||||
if (_isVisible)
|
||||
{
|
||||
AdjustPosition(evt.position);
|
||||
// 로컬 좌표를 root 좌표로 변환
|
||||
var rootPosition = element.LocalToWorld(evt.localPosition);
|
||||
if (_root != null)
|
||||
{
|
||||
rootPosition = _root.WorldToLocal(rootPosition);
|
||||
}
|
||||
AdjustPosition(rootPosition);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit.Modal;
|
||||
@@ -18,10 +19,10 @@ namespace UVC.UIToolkit
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private static VisualElement? _root;
|
||||
|
||||
private bool _disposed;
|
||||
private UTKModalBlocker? _blocker;
|
||||
private VisualElement? _iconContainer;
|
||||
private Label? _iconLabel;
|
||||
private Label? _titleLabel;
|
||||
private Label? _messageLabel;
|
||||
private VisualElement? _buttonContainer;
|
||||
@@ -29,6 +30,11 @@ namespace UVC.UIToolkit
|
||||
private string _title = "";
|
||||
private string _message = "";
|
||||
private AlertType _alertType = AlertType.Info;
|
||||
private string _confirmLabel = "OK";
|
||||
private string _cancelLabel = "Cancel";
|
||||
|
||||
private EventCallback<KeyDownEvent>? _keyDownCallback;
|
||||
private VisualElement? _keyEventTarget;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -116,45 +122,234 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// 기본 루트 요소 설정
|
||||
/// </summary>
|
||||
/// <param name="root">Alert를 표시할 기본 루트 요소</param>
|
||||
public static void SetRoot(VisualElement root)
|
||||
{
|
||||
_root = root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 기본 루트 요소 반환
|
||||
/// </summary>
|
||||
public static VisualElement? GetRoot() => _root;
|
||||
#endregion
|
||||
|
||||
#region Static Factory (without parent)
|
||||
/// <summary>
|
||||
/// Info 알림 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKAlert ShowInfo(string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, title, message, AlertType.Info, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Success 알림 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKAlert ShowSuccess(string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, title, message, AlertType.Success, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning 알림 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKAlert ShowWarning(string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, title, message, AlertType.Warning, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error 알림 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKAlert ShowError(string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, title, message, AlertType.Error, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirm 대화상자 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKAlert ShowConfirm(string title, string message, Action? onConfirm, Action? onCancel = null, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
ValidateRoot();
|
||||
var alert = Show(_root!, title, message, AlertType.Confirm, null, closeOnBlockerClick, confirmLabel, cancelLabel);
|
||||
alert.OnConfirm = onConfirm;
|
||||
alert.OnCancel = onCancel;
|
||||
return alert;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 알림 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKAlert Show(string title, string message, AlertType type = AlertType.Info, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, title, message, type, onClose, closeOnBlockerClick, confirmLabel, cancelLabel);
|
||||
}
|
||||
|
||||
private static void ValidateRoot()
|
||||
{
|
||||
if (_root == null)
|
||||
{
|
||||
throw new InvalidOperationException("UTKAlert.SetRoot()를 먼저 호출하여 기본 루트 요소를 설정해야 합니다.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory Async (without parent)
|
||||
/// <summary>
|
||||
/// Info 알림 표시 (비동기, SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UniTask ShowInfoAsync(string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return ShowAsync(_root!, title, message, AlertType.Info, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Success 알림 표시 (비동기, SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UniTask ShowSuccessAsync(string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return ShowAsync(_root!, title, message, AlertType.Success, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning 알림 표시 (비동기, SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UniTask ShowWarningAsync(string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return ShowAsync(_root!, title, message, AlertType.Warning, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error 알림 표시 (비동기, SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UniTask ShowErrorAsync(string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
ValidateRoot();
|
||||
return ShowAsync(_root!, title, message, AlertType.Error, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirm 대화상자 표시 (비동기, SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
/// <returns>확인 버튼 클릭 시 true, 취소 버튼 클릭 시 false</returns>
|
||||
public static UniTask<bool> ShowConfirmAsync(string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
ValidateRoot();
|
||||
return ShowConfirmAsync(_root!, title, message, closeOnBlockerClick, confirmLabel, cancelLabel);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory Async (with parent)
|
||||
/// <summary>
|
||||
/// Info 알림 표시 (비동기)
|
||||
/// </summary>
|
||||
public static UniTask ShowInfoAsync(VisualElement parent, string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return ShowAsync(parent, title, message, AlertType.Info, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Success 알림 표시 (비동기)
|
||||
/// </summary>
|
||||
public static UniTask ShowSuccessAsync(VisualElement parent, string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return ShowAsync(parent, title, message, AlertType.Success, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning 알림 표시 (비동기)
|
||||
/// </summary>
|
||||
public static UniTask ShowWarningAsync(VisualElement parent, string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return ShowAsync(parent, title, message, AlertType.Warning, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error 알림 표시 (비동기)
|
||||
/// </summary>
|
||||
public static UniTask ShowErrorAsync(VisualElement parent, string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return ShowAsync(parent, title, message, AlertType.Error, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirm 대화상자 표시 (비동기)
|
||||
/// </summary>
|
||||
/// <returns>확인 버튼 클릭 시 true, 취소 버튼 클릭 시 false</returns>
|
||||
public static UniTask<bool> ShowConfirmAsync(VisualElement parent, string title, string message, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<bool>();
|
||||
var alert = Show(parent, title, message, AlertType.Confirm, null, closeOnBlockerClick, confirmLabel, cancelLabel);
|
||||
alert.OnConfirm = () => tcs.TrySetResult(true);
|
||||
alert.OnCancel = () => tcs.TrySetResult(false);
|
||||
alert.OnClosed = () => tcs.TrySetResult(false);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 알림 표시 (비동기)
|
||||
/// </summary>
|
||||
public static UniTask ShowAsync(VisualElement parent, string title, string message, AlertType type, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
var alert = Show(parent, title, message, type, () => tcs.TrySetResult(), closeOnBlockerClick, confirmLabel, cancelLabel);
|
||||
return tcs.Task;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory (with parent)
|
||||
/// <summary>
|
||||
/// Info 알림 표시
|
||||
/// </summary>
|
||||
public static UTKAlert ShowInfo(VisualElement parent, string title, string message, Action? onClose = null)
|
||||
public static UTKAlert ShowInfo(VisualElement parent, string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return Show(parent, title, message, AlertType.Info, onClose);
|
||||
return Show(parent, title, message, AlertType.Info, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Success 알림 표시
|
||||
/// </summary>
|
||||
public static UTKAlert ShowSuccess(VisualElement parent, string title, string message, Action? onClose = null)
|
||||
public static UTKAlert ShowSuccess(VisualElement parent, string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return Show(parent, title, message, AlertType.Success, onClose);
|
||||
return Show(parent, title, message, AlertType.Success, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning 알림 표시
|
||||
/// </summary>
|
||||
public static UTKAlert ShowWarning(VisualElement parent, string title, string message, Action? onClose = null)
|
||||
public static UTKAlert ShowWarning(VisualElement parent, string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return Show(parent, title, message, AlertType.Warning, onClose);
|
||||
return Show(parent, title, message, AlertType.Warning, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error 알림 표시
|
||||
/// </summary>
|
||||
public static UTKAlert ShowError(VisualElement parent, string title, string message, Action? onClose = null)
|
||||
public static UTKAlert ShowError(VisualElement parent, string title, string message, Action? onClose = null, bool closeOnBlockerClick = false, string confirmLabel = "OK")
|
||||
{
|
||||
return Show(parent, title, message, AlertType.Error, onClose);
|
||||
return Show(parent, title, message, AlertType.Error, onClose, closeOnBlockerClick, confirmLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirm 대화상자 표시
|
||||
/// </summary>
|
||||
public static UTKAlert ShowConfirm(VisualElement parent, string title, string message, Action? onConfirm, Action? onCancel = null)
|
||||
public static UTKAlert ShowConfirm(VisualElement parent, string title, string message, Action? onConfirm, Action? onCancel = null, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
var alert = Show(parent, title, message, AlertType.Confirm, null);
|
||||
var alert = Show(parent, title, message, AlertType.Confirm, null, closeOnBlockerClick, confirmLabel, cancelLabel);
|
||||
alert.OnConfirm = onConfirm;
|
||||
alert.OnCancel = onCancel;
|
||||
return alert;
|
||||
@@ -163,13 +358,32 @@ namespace UVC.UIToolkit
|
||||
/// <summary>
|
||||
/// 알림 표시
|
||||
/// </summary>
|
||||
public static UTKAlert Show(VisualElement parent, string title, string message, AlertType type, Action? onClose)
|
||||
/// <param name="parent">부모 요소</param>
|
||||
/// <param name="title">제목</param>
|
||||
/// <param name="message">메시지</param>
|
||||
/// <param name="type">알림 유형</param>
|
||||
/// <param name="onClose">닫힘 콜백</param>
|
||||
/// <param name="closeOnBlockerClick">배경 클릭 시 닫힘 여부</param>
|
||||
/// <param name="confirmLabel">확인 버튼 레이블</param>
|
||||
/// <param name="cancelLabel">취소 버튼 레이블 (Confirm 타입에서만 사용)</param>
|
||||
public static UTKAlert Show(VisualElement parent, string title, string message, AlertType type, Action? onClose, bool closeOnBlockerClick = false, string confirmLabel = "OK", string cancelLabel = "Cancel")
|
||||
{
|
||||
var alert = new UTKAlert(title, message, type);
|
||||
alert.OnClosed = onClose;
|
||||
alert._confirmLabel = confirmLabel;
|
||||
alert._cancelLabel = cancelLabel;
|
||||
alert.UpdateButtons();
|
||||
|
||||
alert._blocker = UTKModalBlocker.Show(parent, 0.5f, true);
|
||||
alert._blocker.OnBlockerClicked += alert.Close;
|
||||
alert._blocker = UTKModalBlocker.Show(parent, 0.5f, closeOnBlockerClick);
|
||||
if (closeOnBlockerClick)
|
||||
{
|
||||
alert._blocker.OnBlockerClicked += alert.Close;
|
||||
}
|
||||
else
|
||||
{
|
||||
// closeOnBlockerClick이 false일 때 blocker 클릭 시 Alert로 포커스 유지
|
||||
alert._blocker.OnBlockerClicked += () => alert.Focus();
|
||||
}
|
||||
alert._blocker.Add(alert);
|
||||
|
||||
// 중앙 정렬
|
||||
@@ -178,6 +392,13 @@ namespace UVC.UIToolkit
|
||||
alert.style.top = Length.Percent(50);
|
||||
alert.style.translate = new Translate(Length.Percent(-50), Length.Percent(-50));
|
||||
|
||||
// ESC 키 이벤트 등록
|
||||
alert.RegisterEscapeKey(parent);
|
||||
|
||||
// Alert에 포커스 설정 (키보드 이벤트 수신을 위해)
|
||||
alert.focusable = true;
|
||||
alert.schedule.Execute(() => alert.Focus());
|
||||
|
||||
return alert;
|
||||
}
|
||||
#endregion
|
||||
@@ -187,26 +408,21 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
AddToClassList("utk-alert");
|
||||
|
||||
_iconContainer = new VisualElement { name = "icon-container" };
|
||||
_iconContainer.AddToClassList("utk-alert__icon-container");
|
||||
Add(_iconContainer);
|
||||
|
||||
_iconLabel = new Label { name = "icon" };
|
||||
_iconLabel.AddToClassList("utk-alert__icon");
|
||||
_iconContainer.Add(_iconLabel);
|
||||
// Title (상단 왼쪽)
|
||||
_titleLabel = new Label { name = "title" };
|
||||
_titleLabel.AddToClassList("utk-alert__title");
|
||||
Add(_titleLabel);
|
||||
|
||||
// Content (가운데 메시지)
|
||||
var contentContainer = new VisualElement { name = "content" };
|
||||
contentContainer.AddToClassList("utk-alert__content");
|
||||
Add(contentContainer);
|
||||
|
||||
_titleLabel = new Label { name = "title" };
|
||||
_titleLabel.AddToClassList("utk-alert__title");
|
||||
contentContainer.Add(_titleLabel);
|
||||
|
||||
_messageLabel = new Label { name = "message" };
|
||||
_messageLabel.AddToClassList("utk-alert__message");
|
||||
contentContainer.Add(_messageLabel);
|
||||
|
||||
// Buttons
|
||||
_buttonContainer = new VisualElement { name = "buttons" };
|
||||
_buttonContainer.AddToClassList("utk-alert__buttons");
|
||||
Add(_buttonContainer);
|
||||
@@ -248,19 +464,6 @@ namespace UVC.UIToolkit
|
||||
};
|
||||
AddToClassList(typeClass);
|
||||
|
||||
// 아이콘 업데이트
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.text = _alertType switch
|
||||
{
|
||||
AlertType.Success => "✓",
|
||||
AlertType.Warning => "⚠",
|
||||
AlertType.Error => "✕",
|
||||
AlertType.Confirm => "?",
|
||||
_ => "ℹ"
|
||||
};
|
||||
}
|
||||
|
||||
// 버튼 업데이트
|
||||
UpdateButtons();
|
||||
}
|
||||
@@ -273,7 +476,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_alertType == AlertType.Confirm)
|
||||
{
|
||||
var cancelBtn = new UTKButton("Cancel", "", UTKButton.ButtonVariant.Normal);
|
||||
var cancelBtn = new UTKButton(_cancelLabel, "", UTKButton.ButtonVariant.Normal);
|
||||
cancelBtn.AddToClassList("utk-alert__btn");
|
||||
cancelBtn.OnClicked += () =>
|
||||
{
|
||||
@@ -282,7 +485,7 @@ namespace UVC.UIToolkit
|
||||
};
|
||||
_buttonContainer.Add(cancelBtn);
|
||||
|
||||
var confirmBtn = new UTKButton("OK", "", UTKButton.ButtonVariant.Primary);
|
||||
var confirmBtn = new UTKButton(_confirmLabel, "", UTKButton.ButtonVariant.Primary);
|
||||
confirmBtn.AddToClassList("utk-alert__btn");
|
||||
confirmBtn.OnClicked += () =>
|
||||
{
|
||||
@@ -293,18 +496,64 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
else
|
||||
{
|
||||
var okBtn = new UTKButton("OK", "", UTKButton.ButtonVariant.Primary);
|
||||
var okBtn = new UTKButton(_confirmLabel, "", UTKButton.ButtonVariant.Primary);
|
||||
okBtn.AddToClassList("utk-alert__btn");
|
||||
okBtn.OnClicked += Close;
|
||||
_buttonContainer.Add(okBtn);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC 키 이벤트 등록
|
||||
/// </summary>
|
||||
private void RegisterEscapeKey(VisualElement parent)
|
||||
{
|
||||
_keyEventTarget = parent;
|
||||
_keyDownCallback = evt =>
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
evt.StopPropagation();
|
||||
HandleEscapeKey();
|
||||
}
|
||||
};
|
||||
|
||||
// 패널에 키 이벤트 등록 (포커스와 관계없이 캡처)
|
||||
_keyEventTarget.RegisterCallback(_keyDownCallback, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC 키 해제
|
||||
/// </summary>
|
||||
private void UnregisterEscapeKey()
|
||||
{
|
||||
if (_keyDownCallback != null && _keyEventTarget != null)
|
||||
{
|
||||
_keyEventTarget.UnregisterCallback(_keyDownCallback, TrickleDown.TrickleDown);
|
||||
_keyDownCallback = null;
|
||||
_keyEventTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC 키 처리
|
||||
/// </summary>
|
||||
private void HandleEscapeKey()
|
||||
{
|
||||
if (_alertType == AlertType.Confirm)
|
||||
{
|
||||
// Confirm 타입: 취소 처리
|
||||
OnCancel?.Invoke();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 알림 닫기
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
UnregisterEscapeKey();
|
||||
OnClosed?.Invoke();
|
||||
if (_blocker != null)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,36 @@ namespace UVC.UIToolkit.Modal
|
||||
/// UIToolkit 기반 컬러 피커 모달
|
||||
/// HSV 색공간 + RGB 슬라이더 + Hex 입력 지원
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 기본 사용법 (Alpha 포함)
|
||||
/// var picker = UTKColorPicker.Show(rootVisualElement, Color.red, "Select Color");
|
||||
/// picker.OnColorChanged += (color) =>
|
||||
/// {
|
||||
/// // 실시간 색상 변경 시 호출
|
||||
/// Debug.Log($"Color changing: {color}");
|
||||
/// };
|
||||
/// picker.OnColorSelected += (color) =>
|
||||
/// {
|
||||
/// // OK 버튼 클릭 시 호출
|
||||
/// Debug.Log($"Color selected: #{ColorUtility.ToHtmlStringRGBA(color)}");
|
||||
/// };
|
||||
///
|
||||
/// // Alpha 없이 사용
|
||||
/// var pickerNoAlpha = UTKColorPicker.Show(rootVisualElement, Color.blue, "Select Color", useAlpha: false);
|
||||
///
|
||||
/// // async/await 사용법 (UniTask)
|
||||
/// Color selectedColor = await UTKColorPicker.ShowAsync(rootVisualElement, Color.red, "Select Color");
|
||||
/// // OK 클릭 시 선택된 색상 반환, 취소/닫기 시 initialColor(Color.red) 반환
|
||||
/// Debug.Log($"Result: #{ColorUtility.ToHtmlStringRGBA(selectedColor)}");
|
||||
///
|
||||
/// // 인스턴스 직접 생성
|
||||
/// var colorPicker = new UTKColorPicker();
|
||||
/// colorPicker.UseAlpha = true; // Alpha 슬라이더 표시 여부 (기본값: true)
|
||||
/// colorPicker.SetColor(Color.green);
|
||||
/// container.Add(colorPicker);
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKColorPicker : VisualElement, IDisposable
|
||||
{
|
||||
@@ -22,7 +52,7 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _useAlpha;
|
||||
private bool _useAlpha = true;
|
||||
private bool _isUpdating; // 재귀 업데이트 방지
|
||||
|
||||
private Color _originalColor = Color.white;
|
||||
@@ -61,9 +91,15 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
private Label? _titleLabel;
|
||||
private Button? _closeButton;
|
||||
private Button? _cancelButton;
|
||||
private Button? _confirmButton;
|
||||
private UTKButton? _cancelButton;
|
||||
private UTKButton? _confirmButton;
|
||||
private VisualElement? _alphaRow;
|
||||
private VisualElement? _header;
|
||||
|
||||
// 드래그 관련 필드
|
||||
private bool _isDragging;
|
||||
private Vector2 _dragStartPosition;
|
||||
private Vector2 _dragStartMousePosition;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -77,6 +113,23 @@ namespace UVC.UIToolkit.Modal
|
||||
public event Action? OnClosed;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// 알파(투명도) 값을 다룰지 여부 (기본값: true)
|
||||
/// </summary>
|
||||
[UxmlAttribute]
|
||||
public bool UseAlpha
|
||||
{
|
||||
get => _useAlpha;
|
||||
set
|
||||
{
|
||||
_useAlpha = value;
|
||||
SetAlphaVisible(value);
|
||||
UpdateHexField();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKColorPicker()
|
||||
{
|
||||
@@ -152,7 +205,7 @@ namespace UVC.UIToolkit.Modal
|
||||
VisualElement parent,
|
||||
Color initialColor,
|
||||
string title = "Color Picker",
|
||||
bool useAlpha = false)
|
||||
bool useAlpha = true)
|
||||
{
|
||||
var picker = new UTKColorPicker();
|
||||
picker._useAlpha = useAlpha;
|
||||
@@ -175,6 +228,41 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 컬러 피커를 표시하고 색상 선택을 기다립니다.
|
||||
/// OK 버튼 클릭 시 선택된 색상을 반환하고, 취소/닫기 시 initialColor를 반환합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 VisualElement</param>
|
||||
/// <param name="initialColor">초기 색상</param>
|
||||
/// <param name="title">피커 제목</param>
|
||||
/// <param name="useAlpha">알파 채널 사용 여부</param>
|
||||
/// <returns>선택된 색상 또는 초기 색상</returns>
|
||||
public static async UniTask<Color> ShowAsync(
|
||||
VisualElement parent,
|
||||
Color initialColor,
|
||||
string title = "Color Picker",
|
||||
bool useAlpha = true)
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<Color>();
|
||||
Color resultColor = initialColor;
|
||||
|
||||
var picker = Show(parent, initialColor, title, useAlpha);
|
||||
|
||||
picker.OnColorSelected += (color) =>
|
||||
{
|
||||
resultColor = color;
|
||||
tcs.TrySetResult(resultColor);
|
||||
};
|
||||
|
||||
picker.OnClosed += () =>
|
||||
{
|
||||
// OnColorSelected가 먼저 호출된 경우 이미 완료됨
|
||||
tcs.TrySetResult(resultColor);
|
||||
};
|
||||
|
||||
return await tcs.Task;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
@@ -222,8 +310,8 @@ namespace UVC.UIToolkit.Modal
|
||||
AddToClassList("utk-color-picker");
|
||||
|
||||
// Header
|
||||
var header = new VisualElement { name = "header" };
|
||||
header.AddToClassList("utk-color-picker__header");
|
||||
_header = new VisualElement { name = "header" };
|
||||
_header.AddToClassList("utk-color-picker__header");
|
||||
|
||||
_titleLabel = new Label("Color Picker") { name = "title" };
|
||||
_titleLabel.AddToClassList("utk-color-picker__title");
|
||||
@@ -231,9 +319,9 @@ namespace UVC.UIToolkit.Modal
|
||||
_closeButton = new Button { name = "close-btn", text = "\u2715" }; // ✕
|
||||
_closeButton.AddToClassList("utk-color-picker__close-btn");
|
||||
|
||||
header.Add(_titleLabel);
|
||||
header.Add(_closeButton);
|
||||
Add(header);
|
||||
_header.Add(_titleLabel);
|
||||
_header.Add(_closeButton);
|
||||
Add(_header);
|
||||
|
||||
// SV Box + Hue Bar row
|
||||
var colorArea = new VisualElement { name = "color-area" };
|
||||
@@ -309,10 +397,10 @@ namespace UVC.UIToolkit.Modal
|
||||
var buttonRow = new VisualElement { name = "button-row" };
|
||||
buttonRow.AddToClassList("utk-color-picker__button-row");
|
||||
|
||||
_cancelButton = new Button { name = "cancel-btn", text = "Cancel" };
|
||||
_cancelButton = new UTKButton("Cancel", "", UTKButton.ButtonVariant.Normal) { name = "cancel-btn" };
|
||||
_cancelButton.AddToClassList("utk-color-picker__cancel-btn");
|
||||
|
||||
_confirmButton = new Button { name = "confirm-btn", text = "OK" };
|
||||
_confirmButton = new UTKButton("OK", "", UTKButton.ButtonVariant.Primary) { name = "confirm-btn" };
|
||||
_confirmButton.AddToClassList("utk-color-picker__confirm-btn");
|
||||
|
||||
buttonRow.Add(_cancelButton);
|
||||
@@ -362,17 +450,35 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
_titleLabel ??= this.Q<Label>("title");
|
||||
_closeButton ??= this.Q<Button>("close-btn");
|
||||
_cancelButton ??= this.Q<Button>("cancel-btn");
|
||||
_confirmButton ??= this.Q<Button>("confirm-btn");
|
||||
_cancelButton ??= this.Q<UTKButton>("cancel-btn");
|
||||
_confirmButton ??= this.Q<UTKButton>("confirm-btn");
|
||||
_alphaRow ??= this.Q<VisualElement>("row-a");
|
||||
}
|
||||
|
||||
private void SetupEvents()
|
||||
{
|
||||
// Close/Cancel/Confirm buttons
|
||||
_closeButton?.RegisterCallback<ClickEvent>(_ => Cancel());
|
||||
_cancelButton?.RegisterCallback<ClickEvent>(_ => Cancel());
|
||||
_confirmButton?.RegisterCallback<ClickEvent>(_ => Confirm());
|
||||
_closeButton?.RegisterCallback<ClickEvent>(evt =>
|
||||
{
|
||||
Cancel();
|
||||
evt.StopPropagation();
|
||||
});
|
||||
|
||||
if (_cancelButton != null)
|
||||
{
|
||||
_cancelButton.OnClicked += () =>
|
||||
{
|
||||
Cancel();
|
||||
};
|
||||
}
|
||||
|
||||
if (_confirmButton != null)
|
||||
{
|
||||
_confirmButton.OnClicked += () =>
|
||||
{
|
||||
Confirm();
|
||||
};
|
||||
}
|
||||
|
||||
// SV Box interaction
|
||||
_svBox?.RegisterCallback<PointerDownEvent>(OnSVBoxPointerDown);
|
||||
@@ -401,6 +507,11 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
// Original preview click - 원래 색상으로 복원
|
||||
_previewOriginal?.RegisterCallback<ClickEvent>(_ => SetColor(_originalColor));
|
||||
|
||||
// Header drag events
|
||||
_header?.RegisterCallback<PointerDownEvent>(OnHeaderPointerDown);
|
||||
_header?.RegisterCallback<PointerMoveEvent>(OnHeaderPointerMove);
|
||||
_header?.RegisterCallback<PointerUpEvent>(OnHeaderPointerUp);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -408,6 +519,38 @@ namespace UVC.UIToolkit.Modal
|
||||
private bool _svDragging;
|
||||
private bool _hueDragging;
|
||||
|
||||
private void OnHeaderPointerDown(PointerDownEvent evt)
|
||||
{
|
||||
// 닫기 버튼 클릭은 무시
|
||||
if (evt.target == _closeButton) return;
|
||||
|
||||
_isDragging = true;
|
||||
_dragStartMousePosition = evt.position;
|
||||
_dragStartPosition = new Vector2(resolvedStyle.left, resolvedStyle.top);
|
||||
_header?.CapturePointer(evt.pointerId);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private void OnHeaderPointerMove(PointerMoveEvent evt)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
Vector2 delta = (Vector2)evt.position - _dragStartMousePosition;
|
||||
style.left = _dragStartPosition.x + delta.x;
|
||||
style.top = _dragStartPosition.y + delta.y;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private void OnHeaderPointerUp(PointerUpEvent evt)
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
_isDragging = false;
|
||||
_header?.ReleasePointer(evt.pointerId);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSVBoxPointerDown(PointerDownEvent evt)
|
||||
{
|
||||
if (_hueDragging) return; // Hue 드래그 중이면 무시
|
||||
@@ -802,8 +945,10 @@ namespace UVC.UIToolkit.Modal
|
||||
{
|
||||
if (_hexField == null) return;
|
||||
|
||||
// 항상 RGBA 형식 (알파값 포함)
|
||||
_hexField.value = ColorUtility.ToHtmlStringRGBA(_currentColor);
|
||||
// UseAlpha에 따라 RGBA(8자리) 또는 RGB(6자리) 형식으로 표시
|
||||
_hexField.value = _useAlpha
|
||||
? ColorUtility.ToHtmlStringRGBA(_currentColor)
|
||||
: ColorUtility.ToHtmlStringRGB(_currentColor);
|
||||
}
|
||||
|
||||
private void UpdateIndicators()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.Locale;
|
||||
@@ -12,6 +13,57 @@ namespace UVC.UIToolkit.Modal
|
||||
/// UIToolkit 기반 날짜/시간 피커 모달
|
||||
/// 캘린더 그리드 + 시간 선택 지원
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 기본 사용법 (날짜만)
|
||||
/// var picker = UTKDatePicker.Show(rootVisualElement, DateTime.Today, UTKDatePicker.PickerMode.DateOnly, "Select Date");
|
||||
/// picker.OnDateSelected += (date) =>
|
||||
/// {
|
||||
/// Debug.Log($"Date selected: {date:yyyy-MM-dd}");
|
||||
/// };
|
||||
///
|
||||
/// // 날짜 + 시간 선택
|
||||
/// var dateTimePicker = UTKDatePicker.Show(rootVisualElement, DateTime.Now, UTKDatePicker.PickerMode.DateAndTime, "Select Date & Time");
|
||||
/// dateTimePicker.OnDateSelected += (date) =>
|
||||
/// {
|
||||
/// Debug.Log($"DateTime selected: {date:yyyy-MM-dd HH:mm}");
|
||||
/// };
|
||||
///
|
||||
/// // async/await 사용법 (UniTask)
|
||||
/// DateTime? selectedDate = await UTKDatePicker.ShowAsync(rootVisualElement, DateTime.Today);
|
||||
/// if (selectedDate.HasValue)
|
||||
/// {
|
||||
/// Debug.Log($"Selected: {selectedDate.Value:yyyy-MM-dd}");
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// Debug.Log("Cancelled");
|
||||
/// }
|
||||
///
|
||||
/// // 날짜 범위 선택
|
||||
/// var rangePicker = UTKDatePicker.ShowRange(rootVisualElement, DateTime.Today, DateTime.Today.AddDays(7));
|
||||
/// rangePicker.OnDateRangeSelected += (start, end) =>
|
||||
/// {
|
||||
/// Debug.Log($"Range selected: {start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}");
|
||||
/// };
|
||||
///
|
||||
/// // 날짜 범위 async/await 사용법
|
||||
/// var result = await UTKDatePicker.ShowRangeAsync(rootVisualElement, DateTime.Today, DateTime.Today.AddDays(7));
|
||||
/// if (result.HasValue)
|
||||
/// {
|
||||
/// Debug.Log($"Range: {result.Value.Start:yyyy-MM-dd} ~ {result.Value.End:yyyy-MM-dd}");
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// Debug.Log("Cancelled");
|
||||
/// }
|
||||
///
|
||||
/// // 인스턴스 직접 생성
|
||||
/// var datePicker = new UTKDatePicker();
|
||||
/// datePicker.SetDate(DateTime.Today);
|
||||
/// container.Add(datePicker);
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKDatePicker : VisualElement, IDisposable
|
||||
{
|
||||
@@ -19,7 +71,18 @@ namespace UVC.UIToolkit.Modal
|
||||
public enum PickerMode
|
||||
{
|
||||
DateOnly,
|
||||
DateAndTime
|
||||
DateAndTime,
|
||||
DateRange,
|
||||
DateTimeRange
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 범위 선택 시 현재 선택 중인 날짜 타입
|
||||
/// </summary>
|
||||
private enum RangeSelectionState
|
||||
{
|
||||
SelectingStart,
|
||||
SelectingEnd
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -36,9 +99,17 @@ namespace UVC.UIToolkit.Modal
|
||||
private DateTime _selectedDate = DateTime.Today;
|
||||
private DateTime _displayMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
|
||||
|
||||
// 범위 선택 관련 필드
|
||||
private DateTime? _rangeStartDate;
|
||||
private DateTime? _rangeEndDate;
|
||||
private RangeSelectionState _rangeState = RangeSelectionState.SelectingStart;
|
||||
|
||||
private UTKModalBlocker? _blocker;
|
||||
private readonly List<Button> _dayButtons = new();
|
||||
|
||||
// 범위 선택 UI 요소
|
||||
private Label? _rangeInfoLabel;
|
||||
|
||||
// UI 요소
|
||||
private Label? _titleLabel;
|
||||
private Button? _closeButton;
|
||||
@@ -51,14 +122,17 @@ namespace UVC.UIToolkit.Modal
|
||||
private VisualElement? _timeRow;
|
||||
private UTKNumberStepper? _hourStepper;
|
||||
private UTKNumberStepper? _minuteStepper;
|
||||
private Button? _cancelButton;
|
||||
private Button? _confirmButton;
|
||||
private UTKButton? _cancelButton;
|
||||
private UTKButton? _confirmButton;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>날짜가 선택되었을 때 발생</summary>
|
||||
public event Action<DateTime>? OnDateSelected;
|
||||
|
||||
/// <summary>날짜 범위가 선택되었을 때 발생 (시작일, 종료일)</summary>
|
||||
public event Action<DateTime, DateTime>? OnDateRangeSelected;
|
||||
|
||||
/// <summary>피커가 닫힐 때 발생</summary>
|
||||
public event Action? OnClosed;
|
||||
#endregion
|
||||
@@ -142,6 +216,155 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 날짜 피커를 표시하고 날짜 선택을 기다립니다.
|
||||
/// OK 버튼 클릭 시 선택된 날짜를 반환하고, 취소/닫기 시 null을 반환합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 VisualElement</param>
|
||||
/// <param name="initialDate">초기 날짜</param>
|
||||
/// <param name="mode">피커 모드 (날짜만 또는 날짜+시간)</param>
|
||||
/// <param name="title">피커 제목</param>
|
||||
/// <returns>선택된 날짜 또는 null (취소 시)</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // async/await 사용법
|
||||
/// DateTime? selectedDate = await UTKDatePicker.ShowAsync(rootVisualElement, DateTime.Today);
|
||||
/// if (selectedDate.HasValue)
|
||||
/// {
|
||||
/// Debug.Log($"Selected: {selectedDate.Value:yyyy-MM-dd}");
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// Debug.Log("Cancelled");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async UniTask<DateTime?> ShowAsync(
|
||||
VisualElement parent,
|
||||
DateTime initialDate,
|
||||
PickerMode mode = PickerMode.DateOnly,
|
||||
string title = "Select Date")
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<DateTime?>();
|
||||
DateTime? resultDate = null;
|
||||
|
||||
var picker = Show(parent, initialDate, mode, title);
|
||||
|
||||
picker.OnDateSelected += (date) =>
|
||||
{
|
||||
resultDate = date;
|
||||
tcs.TrySetResult(resultDate);
|
||||
};
|
||||
|
||||
picker.OnClosed += () =>
|
||||
{
|
||||
// OnDateSelected가 먼저 호출된 경우 이미 완료됨
|
||||
tcs.TrySetResult(resultDate);
|
||||
};
|
||||
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 날짜 범위 피커를 표시합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 VisualElement</param>
|
||||
/// <param name="initialStartDate">초기 시작 날짜 (null이면 오늘)</param>
|
||||
/// <param name="initialEndDate">초기 종료 날짜 (null이면 시작 날짜와 동일)</param>
|
||||
/// <param name="includeTime">시간 선택 포함 여부</param>
|
||||
/// <param name="title">피커 제목</param>
|
||||
public static UTKDatePicker ShowRange(
|
||||
VisualElement parent,
|
||||
DateTime? initialStartDate = null,
|
||||
DateTime? initialEndDate = null,
|
||||
bool includeTime = false,
|
||||
string title = "Select Date Range")
|
||||
{
|
||||
var picker = new UTKDatePicker();
|
||||
picker._mode = includeTime ? PickerMode.DateTimeRange : PickerMode.DateRange;
|
||||
|
||||
var startDate = initialStartDate ?? DateTime.Today;
|
||||
var endDate = initialEndDate ?? startDate;
|
||||
|
||||
// 시작일이 종료일보다 늦으면 스왑
|
||||
if (startDate > endDate)
|
||||
{
|
||||
(startDate, endDate) = (endDate, startDate);
|
||||
}
|
||||
|
||||
picker._rangeStartDate = startDate;
|
||||
picker._rangeEndDate = endDate;
|
||||
picker._rangeState = RangeSelectionState.SelectingStart;
|
||||
picker._displayMonth = new DateTime(startDate.Year, startDate.Month, 1);
|
||||
|
||||
// 블로커 추가
|
||||
picker._blocker = UTKModalBlocker.Show(parent, 0.5f, false);
|
||||
picker._blocker.OnBlockerClicked += picker.Close;
|
||||
|
||||
// 피커 추가
|
||||
parent.Add(picker);
|
||||
|
||||
// UI 초기화
|
||||
picker.SetTitle(title);
|
||||
picker.SetTimeVisible(includeTime);
|
||||
picker.SetRangeInfoVisible(true);
|
||||
picker.UpdateRangeInfo();
|
||||
picker.UpdateCalendar();
|
||||
picker.CenterOnScreen();
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 날짜 범위 피커를 표시하고 범위 선택을 기다립니다.
|
||||
/// OK 버튼 클릭 시 선택된 범위를 반환하고, 취소/닫기 시 null을 반환합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 VisualElement</param>
|
||||
/// <param name="initialStartDate">초기 시작 날짜</param>
|
||||
/// <param name="initialEndDate">초기 종료 날짜</param>
|
||||
/// <param name="includeTime">시간 선택 포함 여부</param>
|
||||
/// <param name="title">피커 제목</param>
|
||||
/// <returns>선택된 범위 (시작일, 종료일) 또는 null (취소 시)</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // async/await 사용법
|
||||
/// var result = await UTKDatePicker.ShowRangeAsync(rootVisualElement);
|
||||
/// if (result.HasValue)
|
||||
/// {
|
||||
/// Debug.Log($"Range: {result.Value.Start:yyyy-MM-dd} ~ {result.Value.End:yyyy-MM-dd}");
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// Debug.Log("Cancelled");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async UniTask<(DateTime Start, DateTime End)?> ShowRangeAsync(
|
||||
VisualElement parent,
|
||||
DateTime? initialStartDate = null,
|
||||
DateTime? initialEndDate = null,
|
||||
bool includeTime = false,
|
||||
string title = "Select Date Range")
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<(DateTime, DateTime)?>();
|
||||
(DateTime, DateTime)? result = null;
|
||||
|
||||
var picker = ShowRange(parent, initialStartDate, initialEndDate, includeTime, title);
|
||||
|
||||
picker.OnDateRangeSelected += (start, end) =>
|
||||
{
|
||||
result = (start, end);
|
||||
tcs.TrySetResult(result);
|
||||
};
|
||||
|
||||
picker.OnClosed += () =>
|
||||
{
|
||||
tcs.TrySetResult(result);
|
||||
};
|
||||
|
||||
return await tcs.Task;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
@@ -304,14 +527,20 @@ namespace UVC.UIToolkit.Modal
|
||||
_timeRow.Add(_minuteStepper);
|
||||
Add(_timeRow);
|
||||
|
||||
// Range Info Label (범위 선택 시 시작일/종료일 표시)
|
||||
_rangeInfoLabel = new Label { name = "range-info" };
|
||||
_rangeInfoLabel.AddToClassList("utk-date-picker__range-info");
|
||||
_rangeInfoLabel.style.display = DisplayStyle.None;
|
||||
Add(_rangeInfoLabel);
|
||||
|
||||
// Buttons
|
||||
var buttonRow = new VisualElement { name = "button-row" };
|
||||
buttonRow.AddToClassList("utk-date-picker__button-row");
|
||||
|
||||
_cancelButton = new Button { name = "cancel-btn", text = "Cancel" };
|
||||
_cancelButton = new UTKButton("Cancel", "", UTKButton.ButtonVariant.Normal) { name = "cancel-btn" };
|
||||
_cancelButton.AddToClassList("utk-date-picker__cancel-btn");
|
||||
|
||||
_confirmButton = new Button { name = "confirm-btn", text = "OK" };
|
||||
_confirmButton = new UTKButton("OK", "", UTKButton.ButtonVariant.Primary) { name = "confirm-btn" };
|
||||
_confirmButton.AddToClassList("utk-date-picker__confirm-btn");
|
||||
|
||||
buttonRow.Add(_cancelButton);
|
||||
@@ -339,8 +568,9 @@ namespace UVC.UIToolkit.Modal
|
||||
_timeRow ??= this.Q<VisualElement>("time-row");
|
||||
_hourStepper ??= this.Q<UTKNumberStepper>("hour-stepper");
|
||||
_minuteStepper ??= this.Q<UTKNumberStepper>("minute-stepper");
|
||||
_cancelButton ??= this.Q<Button>("cancel-btn");
|
||||
_confirmButton ??= this.Q<Button>("confirm-btn");
|
||||
_cancelButton ??= this.Q<UTKButton>("cancel-btn");
|
||||
_confirmButton ??= this.Q<UTKButton>("confirm-btn");
|
||||
_rangeInfoLabel ??= this.Q<Label>("range-info");
|
||||
|
||||
// Day buttons 쿼리
|
||||
if (_dayButtons.Count == 0)
|
||||
@@ -361,8 +591,8 @@ namespace UVC.UIToolkit.Modal
|
||||
private void SetupEvents()
|
||||
{
|
||||
_closeButton?.RegisterCallback<ClickEvent>(_ => Close());
|
||||
_cancelButton?.RegisterCallback<ClickEvent>(_ => Close());
|
||||
_confirmButton?.RegisterCallback<ClickEvent>(_ => Confirm());
|
||||
if (_cancelButton != null) _cancelButton.OnClicked += Close;
|
||||
if (_confirmButton != null) _confirmButton.OnClicked += Confirm;
|
||||
|
||||
_prevYearButton?.RegisterCallback<ClickEvent>(_ => PreviousYear());
|
||||
_prevMonthButton?.RegisterCallback<ClickEvent>(_ => PreviousMonth());
|
||||
@@ -426,6 +656,9 @@ namespace UVC.UIToolkit.Modal
|
||||
btn.RemoveFromClassList("utk-date-picker__day-btn--today");
|
||||
btn.RemoveFromClassList("utk-date-picker__day-btn--selected");
|
||||
btn.RemoveFromClassList("utk-date-picker__day-btn--other-month");
|
||||
btn.RemoveFromClassList("utk-date-picker__day-btn--range-start");
|
||||
btn.RemoveFromClassList("utk-date-picker__day-btn--range-end");
|
||||
btn.RemoveFromClassList("utk-date-picker__day-btn--in-range");
|
||||
|
||||
int dayNumber = i - startDayOfWeek + 1;
|
||||
|
||||
@@ -444,34 +677,76 @@ namespace UVC.UIToolkit.Modal
|
||||
DateTime currentDate = new DateTime(_displayMonth.Year, _displayMonth.Month, dayNumber);
|
||||
DayOfWeek dow = currentDate.DayOfWeek;
|
||||
|
||||
// 선택된 날짜인지 확인
|
||||
bool isSelected = currentDate.Date == _selectedDate.Date;
|
||||
|
||||
// 오늘인지 확인
|
||||
bool isToday = currentDate.Date == today;
|
||||
|
||||
// 선택 상태 클래스
|
||||
if (isSelected)
|
||||
// 범위 선택 모드 처리
|
||||
if (IsRangeMode)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--selected");
|
||||
}
|
||||
bool isRangeStart = _rangeStartDate.HasValue && currentDate.Date == _rangeStartDate.Value.Date;
|
||||
bool isRangeEnd = _rangeEndDate.HasValue && currentDate.Date == _rangeEndDate.Value.Date;
|
||||
bool isInRange = _rangeStartDate.HasValue && _rangeEndDate.HasValue &&
|
||||
currentDate.Date > _rangeStartDate.Value.Date &&
|
||||
currentDate.Date < _rangeEndDate.Value.Date;
|
||||
|
||||
// 오늘 클래스 (선택되지 않은 경우에만)
|
||||
if (isToday && !isSelected)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--today");
|
||||
}
|
||||
|
||||
// 요일별 클래스 (선택되지 않은 경우에만)
|
||||
if (!isSelected)
|
||||
{
|
||||
if (dow == DayOfWeek.Sunday)
|
||||
if (isRangeStart)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--sunday");
|
||||
btn.AddToClassList("utk-date-picker__day-btn--range-start");
|
||||
}
|
||||
else if (dow == DayOfWeek.Saturday)
|
||||
|
||||
if (isRangeEnd)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--saturday");
|
||||
btn.AddToClassList("utk-date-picker__day-btn--range-end");
|
||||
}
|
||||
|
||||
if (isInRange)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--in-range");
|
||||
}
|
||||
|
||||
// 오늘 클래스 (범위에 포함되지 않은 경우에만)
|
||||
bool isToday = currentDate.Date == today;
|
||||
if (isToday && !isRangeStart && !isRangeEnd && !isInRange)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--today");
|
||||
}
|
||||
|
||||
// 요일별 클래스 (범위에 포함되지 않은 경우에만)
|
||||
if (!isRangeStart && !isRangeEnd && !isInRange)
|
||||
{
|
||||
if (dow == DayOfWeek.Sunday)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--sunday");
|
||||
}
|
||||
else if (dow == DayOfWeek.Saturday)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--saturday");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 단일 날짜 선택 모드
|
||||
bool isSelected = currentDate.Date == _selectedDate.Date;
|
||||
bool isToday = currentDate.Date == today;
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--selected");
|
||||
}
|
||||
|
||||
if (isToday && !isSelected)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--today");
|
||||
}
|
||||
|
||||
if (!isSelected)
|
||||
{
|
||||
if (dow == DayOfWeek.Sunday)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--sunday");
|
||||
}
|
||||
else if (dow == DayOfWeek.Saturday)
|
||||
{
|
||||
btn.AddToClassList("utk-date-picker__day-btn--saturday");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,7 +768,7 @@ namespace UVC.UIToolkit.Modal
|
||||
int hour = _hourStepper?.Value ?? _selectedDate.Hour;
|
||||
int minute = _minuteStepper?.Value ?? _selectedDate.Minute;
|
||||
|
||||
_selectedDate = new DateTime(
|
||||
DateTime clickedDate = new DateTime(
|
||||
_displayMonth.Year,
|
||||
_displayMonth.Month,
|
||||
dayNumber,
|
||||
@@ -502,13 +777,63 @@ namespace UVC.UIToolkit.Modal
|
||||
0
|
||||
);
|
||||
|
||||
if (IsRangeMode)
|
||||
{
|
||||
// 범위 선택 모드
|
||||
if (_rangeState == RangeSelectionState.SelectingStart)
|
||||
{
|
||||
// 시작일 선택
|
||||
_rangeStartDate = clickedDate;
|
||||
_rangeEndDate = null;
|
||||
_rangeState = RangeSelectionState.SelectingEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 종료일 선택
|
||||
if (_rangeStartDate.HasValue && clickedDate < _rangeStartDate.Value)
|
||||
{
|
||||
// 클릭한 날짜가 시작일보다 이전이면 시작일로 설정하고 기존 시작일을 종료일로
|
||||
_rangeEndDate = _rangeStartDate;
|
||||
_rangeStartDate = clickedDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rangeEndDate = clickedDate;
|
||||
}
|
||||
_rangeState = RangeSelectionState.SelectingStart;
|
||||
}
|
||||
|
||||
UpdateRangeInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 단일 날짜 선택 모드
|
||||
_selectedDate = clickedDate;
|
||||
}
|
||||
|
||||
UpdateCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
OnDateSelected?.Invoke(GetDate());
|
||||
if (IsRangeMode)
|
||||
{
|
||||
// 범위 선택 모드
|
||||
if (_rangeStartDate.HasValue && _rangeEndDate.HasValue)
|
||||
{
|
||||
OnDateRangeSelected?.Invoke(_rangeStartDate.Value, _rangeEndDate.Value);
|
||||
}
|
||||
else if (_rangeStartDate.HasValue)
|
||||
{
|
||||
// 종료일이 없으면 시작일을 종료일로도 사용
|
||||
OnDateRangeSelected?.Invoke(_rangeStartDate.Value, _rangeStartDate.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDateSelected?.Invoke(GetDate());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -549,6 +874,32 @@ namespace UVC.UIToolkit.Modal
|
||||
style.top = (parentHeight - selfHeight) / 2;
|
||||
});
|
||||
}
|
||||
|
||||
private void SetRangeInfoVisible(bool visible)
|
||||
{
|
||||
if (_rangeInfoLabel != null)
|
||||
{
|
||||
_rangeInfoLabel.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRangeInfo()
|
||||
{
|
||||
if (_rangeInfoLabel == null) return;
|
||||
|
||||
string startText = _rangeStartDate?.ToString("yyyy-MM-dd") ?? "---";
|
||||
string endText = _rangeEndDate?.ToString("yyyy-MM-dd") ?? "---";
|
||||
|
||||
string stateIndicator = _rangeState == RangeSelectionState.SelectingStart ? "▶ " : " ";
|
||||
string endStateIndicator = _rangeState == RangeSelectionState.SelectingEnd ? "▶ " : " ";
|
||||
|
||||
_rangeInfoLabel.text = $"{stateIndicator}시작: {startText} {endStateIndicator}종료: {endText}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 범위 선택 모드인지 확인
|
||||
/// </summary>
|
||||
private bool IsRangeMode => _mode == PickerMode.DateRange || _mode == PickerMode.DateTimeRange;
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
@@ -15,12 +15,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/Modal/UTKToast";
|
||||
private const int DEFAULT_DURATION_MS = 3000;
|
||||
private const int DEFAULT_DURATION_MS = 1000;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private static VisualElement? _root;
|
||||
|
||||
private bool _disposed;
|
||||
private Label? _iconLabel;
|
||||
private Label? _messageLabel;
|
||||
private Button? _closeButton;
|
||||
|
||||
@@ -116,7 +117,78 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory
|
||||
#region Static Methods
|
||||
/// <summary>
|
||||
/// 기본 루트 요소 설정
|
||||
/// </summary>
|
||||
/// <param name="root">Toast를 표시할 기본 루트 요소</param>
|
||||
public static void SetRoot(VisualElement root)
|
||||
{
|
||||
_root = root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 기본 루트 요소 반환
|
||||
/// </summary>
|
||||
public static VisualElement? GetRoot() => _root;
|
||||
|
||||
private static void ValidateRoot()
|
||||
{
|
||||
if (_root == null)
|
||||
{
|
||||
throw new InvalidOperationException("UTKToast.SetRoot()를 먼저 호출하여 기본 루트 요소를 설정해야 합니다.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory (without parent)
|
||||
/// <summary>
|
||||
/// Info 토스트 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKToast ShowInfo(string message, int duration = DEFAULT_DURATION_MS)
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, message, ToastType.Info, duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Success 토스트 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKToast ShowSuccess(string message, int duration = DEFAULT_DURATION_MS)
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, message, ToastType.Success, duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning 토스트 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKToast ShowWarning(string message, int duration = DEFAULT_DURATION_MS)
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, message, ToastType.Warning, duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error 토스트 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKToast ShowError(string message, int duration = DEFAULT_DURATION_MS)
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, message, ToastType.Error, duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 토스트 표시 (SetRoot로 설정된 루트 사용)
|
||||
/// </summary>
|
||||
public static UTKToast Show(string message, ToastType type = ToastType.Info, int duration = DEFAULT_DURATION_MS)
|
||||
{
|
||||
ValidateRoot();
|
||||
return Show(_root!, message, type, duration);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Factory (with parent)
|
||||
/// <summary>
|
||||
/// Info 토스트 표시
|
||||
/// </summary>
|
||||
@@ -180,10 +252,6 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
AddToClassList("utk-toast");
|
||||
|
||||
_iconLabel = new Label { name = "icon" };
|
||||
_iconLabel.AddToClassList("utk-toast__icon");
|
||||
Add(_iconLabel);
|
||||
|
||||
_messageLabel = new Label { name = "message" };
|
||||
_messageLabel.AddToClassList("utk-toast__message");
|
||||
Add(_messageLabel);
|
||||
@@ -228,17 +296,6 @@ namespace UVC.UIToolkit
|
||||
_ => "utk-toast--info"
|
||||
};
|
||||
AddToClassList(typeClass);
|
||||
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.text = _type switch
|
||||
{
|
||||
ToastType.Success => "✓",
|
||||
ToastType.Warning => "⚠",
|
||||
ToastType.Error => "✕",
|
||||
_ => "ℹ"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTaskVoid AutoCloseAsync(int delayMs)
|
||||
|
||||
@@ -12,10 +12,6 @@ namespace UVC.UIToolkit
|
||||
[UxmlElement]
|
||||
public partial class UTKTab : Tab, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/Tab/UTKTab";
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _isEnabled = true;
|
||||
@@ -54,13 +50,6 @@ namespace UVC.UIToolkit
|
||||
public UTKTab() : base()
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
|
||||
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (uss != null)
|
||||
{
|
||||
styleSheets.Add(uss);
|
||||
}
|
||||
|
||||
SetupStyles();
|
||||
SubscribeToThemeChanges();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user