From c021df8012bf2f75c1e56207900e2635cd833904 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:58:06 +0700 Subject: [PATCH] feat: add clear icon styles and functionality for input and textarea components --- .../components/forms/styles/input.style.css | 57 ++++++ .../forms/styles/textarea.style.css | 178 +++++++++++++----- 2 files changed, 185 insertions(+), 50 deletions(-) diff --git a/packages/ui/src/components/forms/styles/input.style.css b/packages/ui/src/components/forms/styles/input.style.css index 4479d8c..1e9d251 100644 --- a/packages/ui/src/components/forms/styles/input.style.css +++ b/packages/ui/src/components/forms/styles/input.style.css @@ -41,6 +41,63 @@ pointer-events: none; } + /* ========================================= + CLEAR ICON + Tombol (x) untuk reset value + ========================================= */ + .rc-input-clear-icon { + /* Reset Button Styles */ + margin: 0; + padding: 0; + background: none; + border: none; + outline: none; + + /* Layout & Sizing */ + display: flex; + align-items: center; + justify-content: center; + font-size: var(--text-sm); /* ~12px sesuai global */ + line-height: 1; + cursor: pointer; + + /* Colors & Transition */ + color: var(--color-gray-400); /* Warna default (abu muda) */ + transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + + /* Visibility Control (Opacity lebih smooth dari display:none jika mau animasi) */ + /* Tapi untuk simplicity sesuai request, kita pakai logic display/visibility standar */ + visibility: visible; + } + + /* Hover State: Lebih gelap agar user tahu bisa diklik */ + .rc-input-clear-icon:hover { + color: var(--color-gray-500); + } + + /* Active/Click State */ + .rc-input-clear-icon:active { + color: var(--color-gray-600); + } + + /* Hidden State */ + .rc-input-clear-icon-hidden { + display: none; + /* Alternatif jika ingin layout tetap terjaga tapi invisible: + visibility: hidden; + pointer-events: none; + */ + } + + /* Placement Helper: + Biasanya clear icon muncul di dalam suffix atau menggantikan suffix. + Jika dia berdiri sendiri sebagai sibling terakhir: + */ + .rc-input-affix-wrapper .rc-input-clear-icon { + margin-left: 0.5rem; /* ~8px spacing dari text */ + z-index: 2; /* Pastikan di atas layer lain */ + } + /* ========================================= AFFIX WRAPPER (PREFIX / SUFFIX) SETUP ========================================= */ diff --git a/packages/ui/src/components/forms/styles/textarea.style.css b/packages/ui/src/components/forms/styles/textarea.style.css index 460f979..5035034 100644 --- a/packages/ui/src/components/forms/styles/textarea.style.css +++ b/packages/ui/src/components/forms/styles/textarea.style.css @@ -1,16 +1,13 @@ @layer components { /* ========================================= - TEXTAREA STYLES + BASE STYLE: TEXTAREA (Standalone) ========================================= */ - .rc-textarea { - /* Tambahan wajib untuk menjamin lebar akurat */ box-sizing: border-box; - display: block; width: 100%; - /* Menggunakan Variable Dimensi Textarea */ + /* Global Variables */ min-height: var(--textarea-min-height); padding: var(--textarea-padding-y) var(--textarea-padding-x); @@ -18,158 +15,239 @@ line-height: 1.5; color: var(--input-text); - /* Default Background */ + /* Default Base (akan di-override jika ada wrapper) */ background-color: var(--input-bg); - border: 1px solid transparent; border-radius: var(--input-radius); - /* Transisi standar */ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); outline: none; resize: vertical; } - /* Placeholder Styling */ .rc-textarea::placeholder { color: var(--input-placeholder); - opacity: 1; /* Firefox fix */ + opacity: 1; } - /* Disabled State */ - .rc-textarea:disabled, - .rc-textarea[disabled] { + /* Disabled State (Standalone) */ + .rc-textarea:disabled { background-color: var(--disabled-bg) !important; color: var(--disabled-text); border-color: var(--disabled-border) !important; cursor: not-allowed; - opacity: 1; /* iOS fix */ - pointer-events: none; } /* ========================================= - WRAPPER (untuk showCount) + WRAPPER SETUP (allowClear / showCount) + Component akan memindahkan class Variant & Status ke div ini ========================================= */ .rc-textarea-affix-wrapper { width: 100%; position: relative; display: inline-block; + + /* Wrapper harus punya base style border/bg agar status works */ + border: 1px solid transparent; + border-radius: var(--input-radius); + background-color: var(--input-bg); + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + } + + /* RESET INNER TEXTAREA */ + /* Saat dibungkus, textarea menjadi transparan, wrapper yang menangani warna */ + .rc-textarea-affix-wrapper .rc-textarea { + background-color: transparent; + border: none; + outline: none; + box-shadow: none !important; /* Hapus shadow default inner */ + /* Padding tetap di textarea agar text flow benar */ + } + + /* Clear Icon Position */ + .rc-textarea-clear-icon { + margin: 0; + padding: 0; + background: none; + border: none; + outline: none; + cursor: pointer; + position: absolute; + top: var(--textarea-padding-y); + right: var(--textarea-padding-x); + z-index: 5; + font-size: var(--text-sm); + line-height: 1; + color: var(--color-gray-400); + transition: color 0.2s; + } + .rc-textarea-clear-icon:hover { + color: var(--color-gray-500); + } + .rc-textarea-clear-icon:active { + color: var(--color-gray-600); + } + .rc-textarea-clear-icon-hidden { + display: none; + } + + /* Padding adjustment jika ada clear icon */ + .rc-textarea-has-clear { + padding-right: calc(var(--textarea-padding-x) + 1.25rem); + } + + /* Disabled Wrapper */ + .rc-textarea-affix-wrapper-disabled { + background-color: var(--disabled-bg) !important; + border-color: var(--disabled-border) !important; + color: var(--disabled-text); + cursor: not-allowed; + pointer-events: none; + } + .rc-textarea-affix-wrapper-disabled .rc-textarea { + cursor: not-allowed; } /* ========================================= - 1. VARIANT: OUTLINED + LOGIC MATRIX: [VARIANT] x [STATUS] + Target: .rc-textarea (standalone) AND .rc-textarea-affix-wrapper (wrapped) ========================================= */ + /* ------------------------------------------------ + 1. VARIANT: OUTLINED + ------------------------------------------------ */ + /* --- Default --- */ - .rc-textarea.textarea-variant-outlined.textarea-status-default { + .textarea-variant-outlined.textarea-status-default { background-color: var(--input-bg); border-color: var(--input-border); } - .rc-textarea.textarea-variant-outlined.textarea-status-default:hover:not(:disabled) { + /* Hover */ + .textarea-variant-outlined.textarea-status-default:hover:not(:disabled):not(.rc-textarea-affix-wrapper-disabled) { border-color: var(--input-border-hover); } - .rc-textarea.textarea-variant-outlined.textarea-status-default:focus { + /* Focus (Standalone OR Wrapper Focused) */ + .rc-textarea.textarea-variant-outlined.textarea-status-default:focus, + .rc-textarea-affix-wrapper.textarea-variant-outlined.textarea-status-default.rc-textarea-affix-wrapper-focused { border-color: var(--focus-border-color); box-shadow: 0 0 0 3px var(--focus-ring-color); + z-index: 1; } /* --- Error --- */ - .rc-textarea.textarea-variant-outlined.textarea-status-error { - background-color: var(--input-bg); + .textarea-variant-outlined.textarea-status-error { border-color: var(--color-error-500); + z-index: 1; } - .rc-textarea.textarea-variant-outlined.textarea-status-error:hover:not(:disabled), - .rc-textarea.textarea-variant-outlined.textarea-status-error:focus { + .textarea-variant-outlined.textarea-status-error:hover:not(:disabled):not(.rc-textarea-affix-wrapper-disabled) { + border-color: var(--color-error-600); + } + .rc-textarea.textarea-variant-outlined.textarea-status-error:focus, + .rc-textarea-affix-wrapper.textarea-variant-outlined.textarea-status-error.rc-textarea-affix-wrapper-focused { border-color: var(--color-error-600); box-shadow: 0 0 0 3px var(--color-error-100); } /* --- Warning --- */ - .rc-textarea.textarea-variant-outlined.textarea-status-warning { - background-color: var(--input-bg); + .textarea-variant-outlined.textarea-status-warning { border-color: var(--color-warning-500); + z-index: 1; } - .rc-textarea.textarea-variant-outlined.textarea-status-warning:hover:not(:disabled), - .rc-textarea.textarea-variant-outlined.textarea-status-warning:focus { + .textarea-variant-outlined.textarea-status-warning:hover:not(:disabled):not(.rc-textarea-affix-wrapper-disabled) { + border-color: var(--color-warning-600); + } + .rc-textarea.textarea-variant-outlined.textarea-status-warning:focus, + .rc-textarea-affix-wrapper.textarea-variant-outlined.textarea-status-warning.rc-textarea-affix-wrapper-focused { border-color: var(--color-warning-600); box-shadow: 0 0 0 3px var(--color-warning-100); } /* --- Success --- */ - .rc-textarea.textarea-variant-outlined.textarea-status-success { - background-color: var(--input-bg); + .textarea-variant-outlined.textarea-status-success { border-color: var(--color-success-500); + z-index: 1; } - .rc-textarea.textarea-variant-outlined.textarea-status-success:hover:not(:disabled), - .rc-textarea.textarea-variant-outlined.textarea-status-success:focus { + .textarea-variant-outlined.textarea-status-success:hover:not(:disabled):not(.rc-textarea-affix-wrapper-disabled) { + border-color: var(--color-success-600); + } + .rc-textarea.textarea-variant-outlined.textarea-status-success:focus, + .rc-textarea-affix-wrapper.textarea-variant-outlined.textarea-status-success.rc-textarea-affix-wrapper-focused { border-color: var(--color-success-600); box-shadow: 0 0 0 3px var(--color-success-100); } /* --- Info --- */ - .rc-textarea.textarea-variant-outlined.textarea-status-info { - background-color: var(--input-bg); + .textarea-variant-outlined.textarea-status-info { border-color: var(--color-info-500); + z-index: 1; } - .rc-textarea.textarea-variant-outlined.textarea-status-info:hover:not(:disabled), - .rc-textarea.textarea-variant-outlined.textarea-status-info:focus { + .textarea-variant-outlined.textarea-status-info:hover:not(:disabled):not(.rc-textarea-affix-wrapper-disabled) { + border-color: var(--color-info-600); + } + .rc-textarea.textarea-variant-outlined.textarea-status-info:focus, + .rc-textarea-affix-wrapper.textarea-variant-outlined.textarea-status-info.rc-textarea-affix-wrapper-focused { border-color: var(--color-info-600); box-shadow: 0 0 0 3px var(--color-info-100); } - /* ========================================= + /* ------------------------------------------------ 2. VARIANT: FILLED - ========================================= */ + ------------------------------------------------ */ /* --- Default Filled --- */ - .rc-textarea.textarea-variant-filled.textarea-status-default { + .textarea-variant-filled.textarea-status-default { background-color: var(--input-bg); border-color: var(--input-border); } - .rc-textarea.textarea-variant-filled.textarea-status-default:hover:not(:disabled) { + .textarea-variant-filled.textarea-status-default:hover:not(:disabled):not(.rc-textarea-affix-wrapper-disabled) { border-color: var(--input-border-hover); } - .rc-textarea.textarea-variant-filled.textarea-status-default:focus { + .rc-textarea.textarea-variant-filled.textarea-status-default:focus, + .rc-textarea-affix-wrapper.textarea-variant-filled.textarea-status-default.rc-textarea-affix-wrapper-focused { border-color: var(--focus-border-color); box-shadow: 0 0 0 3px var(--focus-ring-color); } /* --- Error Filled --- */ - .rc-textarea.textarea-variant-filled.textarea-status-error { + .textarea-variant-filled.textarea-status-error { background-color: var(--color-error-50); border-color: var(--color-error-500); } - .rc-textarea.textarea-variant-filled.textarea-status-error:focus { - background-color: var(--input-bg); /* Reset ke putih saat typing */ + .rc-textarea.textarea-variant-filled.textarea-status-error:focus, + .rc-textarea-affix-wrapper.textarea-variant-filled.textarea-status-error.rc-textarea-affix-wrapper-focused { + background-color: var(--input-bg); box-shadow: 0 0 0 3px var(--color-error-100); } /* --- Warning Filled --- */ - .rc-textarea.textarea-variant-filled.textarea-status-warning { + .textarea-variant-filled.textarea-status-warning { background-color: var(--color-warning-50); border-color: var(--color-warning-500); } - .rc-textarea.textarea-variant-filled.textarea-status-warning:focus { + .rc-textarea.textarea-variant-filled.textarea-status-warning:focus, + .rc-textarea-affix-wrapper.textarea-variant-filled.textarea-status-warning.rc-textarea-affix-wrapper-focused { background-color: var(--input-bg); box-shadow: 0 0 0 3px var(--color-warning-100); } /* --- Success Filled --- */ - .rc-textarea.textarea-variant-filled.textarea-status-success { + .textarea-variant-filled.textarea-status-success { background-color: var(--color-success-50); border-color: var(--color-success-500); } - .rc-textarea.textarea-variant-filled.textarea-status-success:focus { + .rc-textarea.textarea-variant-filled.textarea-status-success:focus, + .rc-textarea-affix-wrapper.textarea-variant-filled.textarea-status-success.rc-textarea-affix-wrapper-focused { background-color: var(--input-bg); box-shadow: 0 0 0 3px var(--color-success-100); } /* --- Info Filled --- */ - .rc-textarea.textarea-variant-filled.textarea-status-info { + .textarea-variant-filled.textarea-status-info { background-color: var(--color-info-50); border-color: var(--color-info-500); } - .rc-textarea.textarea-variant-filled.textarea-status-info:focus { + .rc-textarea.textarea-variant-filled.textarea-status-info:focus, + .rc-textarea-affix-wrapper.textarea-variant-filled.textarea-status-info.rc-textarea-affix-wrapper-focused { background-color: var(--input-bg); box-shadow: 0 0 0 3px var(--color-info-100); }