UNPKG

vanillajs-excelike-table

Version:

A user-friendly pure JavaScript table library with Excel-like features, preset configurations, and intuitive column helpers. Vanilla JS implementation - no frameworks required!

1,300 lines (1,116 loc) 22.7 kB
/* ExceLike Table - VanillaJS Version Styles */ .excelike-table-wrapper { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; width: 100%; background: white; font-size: var(--table-font-size, 14px); } .excelike-table-wrapper * { font-size: inherit; } .excelike-table { position: relative; display: flex; flex-direction: column; height: 100%; overflow: hidden; } .table-loading { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; font-size: 14px; color: rgba(0, 0, 0, 0.65); } .table-container { position: relative; overflow: auto; flex: 1; min-height: 0; } /* Sticky header for fixed height tables */ .table-container.fixed-height { height: 100%; } .table-container.fixed-height .table-header { position: sticky; top: 0; z-index: 10; background-color: #fafafa; } .table { width: 100%; border-collapse: collapse; background-color: #fff; font-size: 14px; table-layout: fixed; } .table.bordered { border: 1px solid #d9d9d9; } .table.small { font-size: 12px; } .table.large { font-size: 16px; } .table-header { background-color: #fafafa; border-bottom: 1px solid #d9d9d9; padding: 0; position: relative; overflow: visible; position: sticky; top: 0; z-index: 100; } /* Enhanced sticky header for better visibility */ .table-container.fixed-height .table-header { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .table.bordered .table-header { border-right: 1px solid #d9d9d9; } .table.bordered .table-header:last-child { border-right: none; } .header-content { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; min-height: 32px; position: relative; } .header-title { font-weight: 600; color: rgba(0, 0, 0, 0.85); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .header-controls { display: flex; align-items: stretch; flex-shrink: 0; height: 100%; } .sort-controls { display: flex; flex-direction: column; align-items: center; gap: 1px; width: 14px; height: 100%; min-height: 28px; } .sort-indicator { width: 14px; height: 100%; min-height: 28px; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s; } .sort-indicator:hover { background: rgba(24, 144, 255, 0.1); border-radius: 2px; } .sort-triangle { width: 0; height: 0; border-style: solid; display: block; margin: 1px 0; } .sort-triangle.up { border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 5px solid rgba(0, 0, 0, 0.45); border-top: none; } .sort-triangle.down { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid rgba(0, 0, 0, 0.45); border-bottom: none; } .sort-triangle.up.active { border-bottom-color: #1890ff; } .sort-triangle.down.active { border-top-color: #1890ff; } .filter-dropdown-container { position: relative; } .filter-btn { background: none; border: none; cursor: pointer; color: rgba(0, 0, 0, 0.45); padding: 2px; width: 14px; height: 100%; min-height: 28px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; border-radius: 2px; } .filter-btn:hover { color: #1890ff; background: rgba(24, 144, 255, 0.1); } .filter-btn.active { color: #1890ff; background: rgba(24, 144, 255, 0.15); } .filter-btn .filter-funnel { width: 12px; height: 12px; position: relative; display: inline-block; } /* Funnel-shaped filter icon: downward triangle + thin rectangle */ .filter-btn .filter-funnel::before { content: ''; position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-style: solid; border-width: 6px 6px 0 6px; border-color: currentColor transparent transparent transparent; } .filter-btn .filter-funnel::after { content: ''; position: absolute; top: 4px; left: 50%; transform: translateX(-50%); width: 2px; height: 8px; background: currentColor; } .filter-dropdown-wrapper { position: fixed; z-index: 9999; min-width: 200px; overflow: visible; } .filter-dropdown { background: white; border: 1px solid #d9d9d9; border-radius: 6px; box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05); min-width: 250px; max-width: 400px; font-size: 14px; position: relative; display: flex; flex-direction: column; overflow: hidden; /* max-height will be set dynamically based on viewport */ } .filter-dropdown-header { padding: 8px 12px; border-bottom: 1px solid #f0f0f0; background: #fafafa; border-radius: 6px 6px 0 0; flex-shrink: 0; } .filter-dropdown-title { font-weight: 500; color: rgba(0, 0, 0, 0.85); } .filter-dropdown-search { padding: 8px 12px; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; } .filter-search-input { width: 100%; padding: 4px 8px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 14px; outline: none; box-sizing: border-box; } .filter-search-input:focus { border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } .filter-dropdown-content { flex: 1; overflow-y: auto; min-height: 0; } .filter-option { padding: 4px 12px; border-bottom: 1px solid #f0f0f0; } .filter-option:last-child { border-bottom: none; } .filter-option.select-all { background: #f9f9f9; font-weight: 500; border-bottom: 1px solid #d9d9d9; } .filter-option-label { display: flex; align-items: center; cursor: pointer; padding: 4px 0; user-select: none; justify-content: flex-start; } .filter-checkbox { margin-right: 8px; cursor: pointer; } .filter-option-text { flex: 1; color: rgba(0, 0, 0, 0.85); text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .filter-option:hover { background: #f5f5f5; } .filter-dropdown-footer { padding: 8px 12px; border-top: 1px solid #f0f0f0; display: flex; flex-direction: column; gap: 8px; flex-shrink: 0; } .filter-footer-actions { display: flex; justify-content: flex-end; gap: 8px; } .filter-btn-confirm { background: #1890ff; color: white; border: none; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500; } .filter-btn-confirm:hover:not(:disabled) { background: #40a9ff; } .filter-btn-confirm:disabled { background: #f5f5f5; color: rgba(0, 0, 0, 0.25); cursor: not-allowed; } .filter-btn-cancel { background: white; color: rgba(0, 0, 0, 0.65); border: 1px solid #d9d9d9; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; } .filter-btn-cancel:hover { color: #1890ff; border-color: #1890ff; } .filter-btn-clear-column { background: white; color: #1890ff; border: 1px solid #1890ff; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: all 0.2s; align-self: flex-start; } .filter-btn-clear-column:hover { background: #e6f7ff; border-color: #40a9ff; color: #40a9ff; } .filter-options-list { /* max-height will be set dynamically in JavaScript */ overflow-y: auto; flex: 1; } /* Range Filter Styles */ .filter-dropdown-range { padding: 8px 12px; border-bottom: 1px solid #f0f0f0; background: #fafafa; } .range-filter { padding: 12px; background: white; border-radius: 4px; font-size: 14px; } .range-inputs { display: flex; gap: 12px; margin-bottom: 16px; } .range-input-group { flex: 1; display: flex; flex-direction: column; gap: 4px; } .range-label { font-size: 12px; color: rgba(0, 0, 0, 0.65); font-weight: 500; } .range-input { width: 100%; padding: 4px 8px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 14px; outline: none; box-sizing: border-box; } .range-input:focus { border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } .range-slider-container { position: relative; padding: 8px 0; } .range-slider { position: relative; height: 6px; background: #f0f0f0; border-radius: 3px; margin: 16px 0; cursor: pointer; } .range-track { position: absolute; top: 0; height: 100%; background: #1890ff; border-radius: 3px; } .range-thumb { position: absolute; top: 50%; transform: translate(-50%, -50%); width: 16px; height: 16px; background: #1890ff; border: 2px solid white; border-radius: 50%; cursor: grab; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); z-index: 2; } .range-thumb:hover { transform: translate(-50%, -50%) scale(1.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } /* Date Range Filter Styles */ .date-range-filter { padding: 12px; background: white; border-radius: 4px; font-size: 14px; } .date-range-inputs { display: flex; gap: 12px; margin-bottom: 16px; } .date-range-input-group { flex: 1; display: flex; flex-direction: column; gap: 4px; } .date-range-label { font-size: 12px; color: rgba(0, 0, 0, 0.65); font-weight: 500; } .date-range-input { width: 100%; padding: 4px 8px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 14px; outline: none; box-sizing: border-box; } .date-range-input:focus { border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } .date-range-slider-container { position: relative; padding: 8px 0; } .date-range-slider { position: relative; height: 6px; background: #f0f0f0; border-radius: 3px; margin: 16px 0; cursor: pointer; } .date-range-track { position: absolute; top: 0; height: 100%; background: #1890ff; border-radius: 3px; } .date-range-thumb { position: absolute; top: 50%; transform: translate(-50%, -50%); width: 16px; height: 16px; background: #1890ff; border: 2px solid white; border-radius: 50%; cursor: grab; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); z-index: 2; } .date-range-thumb:hover { transform: translate(-50%, -50%) scale(1.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } /* Date Filter Styles */ .date-filter-content { flex: 1; overflow-y: auto; min-height: 0; /* max-height will be set dynamically in JavaScript */ } .date-filter-group { border-bottom: 1px solid #f0f0f0; } .date-filter-group:last-child { border-bottom: none; } .date-filter-group .date-filter-group-header { display: flex; align-items: center; padding: 4px 12px; background: #fafafa; font-weight: 500; cursor: pointer; padding-left: 30px !important; /* 年のレベル:30px(展開ボタンのmargin考慮) */ } .date-filter-children { background: white; } .date-filter-item { border-bottom: 1px solid #f0f0f0; /* Base padding for month level items */ } .date-filter-item:last-child { border-bottom: none; } .expand-btn { background: none; border: none; cursor: pointer; font-size: 12px; color: #1890ff; padding: 0; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; margin-right: 4px; border-radius: 2px; } .expand-btn:hover { background: #e6f7ff; } /* Specific styling for month expand buttons to align with month indent */ .expand-btn-month { margin-left: 20px; /* Additional indent for month level to match new spacing */ } .date-filter-label { display: flex; align-items: center; cursor: pointer; padding: 4px 0; user-select: none; flex: 1; justify-content: flex-start; } .date-filter-checkbox { margin-right: 8px; cursor: pointer; } .date-filter-text { flex: 1; color: rgba(0, 0, 0, 0.85); text-align: left; } .date-filter-item:hover { background: #f5f5f5; } .date-filter-group-header:hover { background: #f0f0f0; } .date-filter-month { border-bottom: 1px solid #f0f0f0; } .date-filter-month:last-child { border-bottom: none; } .date-filter-item.date-filter-month .date-filter-month-header { display: flex; align-items: center; padding: 4px 12px; background: #f9f9f9; font-weight: 500; cursor: pointer; padding-left: 32px !important; /* 月のレベル:32px(展開ボタンのmargin考慮) */ } .date-filter-month-header:hover { background: #f0f0f0; } .date-filter-days { background: white; } .date-filter-item.date-filter-day { border-bottom: 1px solid #f0f0f0; padding-left: 92px !important; /* 日のレベル:92px(変更しない) */ } .date-filter-day:last-child { border-bottom: none; } /* Date Expand Controls */ .date-expand-controls { padding: 8px 12px; border-bottom: 1px solid #f0f0f0; background: #fafafa; } .expand-control-header { display: flex; align-items: center; gap: 8px; } .expand-control-label { font-size: 12px; color: rgba(0, 0, 0, 0.65); font-weight: 500; } .expand-control-buttons { display: flex; gap: 4px; } .expand-control-btn { padding: 4px 8px; border: 1px solid #d9d9d9; background: white; border-radius: 4px; cursor: pointer; font-size: 12px; color: rgba(0, 0, 0, 0.65); transition: all 0.2s; min-width: 32px; } .expand-control-btn:hover { border-color: #1890ff; color: #1890ff; } .expand-control-btn.active { background: #1890ff; color: white; border-color: #1890ff; } /* Filter option count styling */ .filter-option-count { color: rgba(0, 0, 0, 0.45); font-size: 11px; font-weight: normal; } /* Column resize handle */ .resize-handle { position: absolute; top: 0; right: 0; bottom: 0; width: 4px; cursor: col-resize; background: transparent; border-right: 2px solid transparent; transition: border-color 0.2s; } .resize-handle:hover { border-right-color: #1890ff; cursor: ew-resize; } .table-cell { padding: 8px 12px; color: rgba(0, 0, 0, 0.85); word-wrap: break-word; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; position: relative; cursor: pointer; } .header-title { font-weight: 600; color: rgba(0, 0, 0, 0.85); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; } /* Tooltip styles */ .cell-tooltip { position: absolute; background: rgba(0, 0, 0, 0.85); color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 10000; pointer-events: none; max-width: 300px; word-wrap: break-word; white-space: normal; } /* Resize tooltip styles */ .resize-tooltip { position: fixed; background: rgba(0, 0, 0, 0.9); color: white; padding: 6px 10px; border-radius: 6px; font-size: 12px; font-weight: 500; z-index: 10000; pointer-events: none; white-space: nowrap; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.2); backdrop-filter: blur(4px); transition: opacity 0.2s ease; } .resize-tooltip::before { content: ''; position: absolute; bottom: -5px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid rgba(0, 0, 0, 0.9); } .table.bordered .table-cell { border-right: 1px solid #d9d9d9; border-bottom: 1px solid #d9d9d9; } .table.bordered .table-cell:last-child { border-right: none; } .table.bordered tr:last-child .table-cell { border-bottom: none; } .table tbody tr:hover { background-color: #f5f5f5; cursor: pointer; } /* Pagination */ .enhanced-table-pagination { display: flex; justify-content: space-between; align-items: center; padding: 16px 0; margin-top: 16px; border-top: 1px solid #d9d9d9; flex-shrink: 0; } .pagination-info { color: rgba(0, 0, 0, 0.65); font-size: 14px; } .pagination-controls { display: flex; align-items: center; gap: 12px; } .pagination-controls button { padding: 6px 12px; border: 1px solid #d9d9d9; background: #fff; cursor: pointer; border-radius: 4px; font-size: 14px; color: rgba(0, 0, 0, 0.65); transition: all 0.2s; } .pagination-controls button:hover:not(:disabled) { border-color: #1890ff; color: #1890ff; } .pagination-controls button:disabled { opacity: 0.5; cursor: not-allowed; } .page-info { color: rgba(0, 0, 0, 0.65); font-size: 14px; } .page-size-selector { display: flex; align-items: center; gap: 4px; } .page-size-select { padding: 4px 8px; border: 1px solid #d9d9d9; border-radius: 4px; background: #fff; font-size: 14px; color: rgba(0, 0, 0, 0.65); cursor: pointer; transition: border-color 0.2s; } .page-size-select:hover { border-color: #1890ff; } .page-size-select:focus { outline: none; border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } /* Table Menu */ .table-menu-container { position: relative; display: flex; justify-content: flex-end; margin-bottom: 8px; flex-shrink: 0; } .table-menu-wrapper { position: relative; } .table-menu-btn { background: #fff; border: 1px solid #d9d9d9; border-radius: 4px; padding: 6px 8px; cursor: pointer; font-size: 16px; transition: all 0.2s; } .table-menu-btn:hover { border-color: #1890ff; color: #1890ff; } .table-menu-dropdown { position: absolute; top: 100%; right: 0; background: white; border: 1px solid #d9d9d9; border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 1000; min-width: 150px; margin-top: 4px; } .table-menu-item { padding: 8px 12px; cursor: pointer; font-size: 14px; transition: background-color 0.2s; } .table-menu-item:hover { background-color: #f5f5f5; } /* Submenu styles */ .table-menu-item-submenu { position: relative; display: flex; align-items: center; justify-content: space-between; } .submenu-arrow { font-size: 10px; color: rgba(0, 0, 0, 0.45); } .table-submenu-dropdown { position: absolute; right: 100%; top: 0; background: white; border: 1px solid #d9d9d9; border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: 100px; display: none; z-index: 1001; margin-right: 0; } .table-menu-item-submenu:hover .table-submenu-dropdown { display: block; } .table-menu-item-current { background-color: #e6f7ff; color: #1890ff; font-weight: 500; } /* Column Settings Modal */ .column-settings-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.25); z-index: 10000; display: flex; align-items: center; justify-content: center; } .column-settings-modal { background: white; border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); width: 400px; max-height: 80vh; display: flex; flex-direction: column; } .column-settings-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid #f0f0f0; } .column-settings-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: rgba(0, 0, 0, 0.85); } .close-btn { background: none; border: none; font-size: 20px; cursor: pointer; color: rgba(0, 0, 0, 0.45); padding: 0; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; border-radius: 2px; } .close-btn:hover { color: rgba(0, 0, 0, 0.75); background: rgba(0, 0, 0, 0.06); } .column-settings-content { flex: 1; overflow-y: auto; padding: 16px 20px; } .column-list { display: flex; flex-direction: column; gap: 8px; } .column-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f0f0f0; } .select-all-item { background: #f9f9f9; padding: 8px 12px; margin: -8px -12px 8px -12px; border-bottom: 1px solid #d9d9d9; font-weight: 500; } .column-label { display: flex; align-items: center; cursor: pointer; user-select: none; flex: 1; } .column-checkbox { margin-right: 8px; cursor: pointer; } .column-text { flex: 1; color: rgba(0, 0, 0, 0.85); font-size: 14px; } .pin-label { display: flex; align-items: center; margin-left: 10px; cursor: pointer; } .pin-checkbox { margin-right: 4px; cursor: pointer; } .pin-icon { font-size: 14px; opacity: 0.6; } .pin-checkbox:checked + .pin-icon { opacity: 1; } .column-settings-footer { padding: 12px 20px; border-top: 1px solid #f0f0f0; display: flex; justify-content: flex-end; gap: 8px; } .confirm-btn { background: #1890ff; color: white; border: none; padding: 6px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500; transition: background-color 0.2s; } .confirm-btn:hover:not(:disabled) { background: #40a9ff; } .cancel-btn { background: white; color: rgba(0, 0, 0, 0.65); border: 1px solid #d9d9d9; padding: 6px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: all 0.2s; } .cancel-btn:hover { color: #1890ff; border-color: #1890ff; } /* Column pinning styles */ .pinned-column { box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1); background-color: #ffffff !important; z-index: 100; } /* Separate styling for pinned header cells */ .table-header.pinned-column { background-color: #fafafa !important; } /* Blue line only on the last pinned column */ .last-pinned-column { border-right: 2px solid #1890ff !important; } /* Scrollbar styling */ .filter-dropdown-content::-webkit-scrollbar { width: 6px; } .filter-dropdown-content::-webkit-scrollbar-track { background: #f1f1f1; } .filter-dropdown-content::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 3px; } .filter-dropdown-content::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } /* Responsive */ @media (max-width: 768px) { .excelike-table-wrapper { font-size: 12px; } .header-content { padding: 6px 8px; } .table-cell { padding: 6px 8px; } .enhanced-table-pagination { flex-direction: column; gap: 12px; align-items: stretch; } .pagination-controls { justify-content: center; flex-wrap: wrap; } } /* Print styles */ @media print { .enhanced-table-pagination { display: none; } .filter-dropdown-wrapper { display: none; } .table-menu-container { display: none; } .column-settings-overlay { display: none; } .table tbody tr:hover { background-color: transparent; } }