Magic Responsive Tables, 자극 및 IntersectionObserver
32113 단어 railswebdevjavascript
열을 추가할 때 문제가 발생했습니다.작은 화면을 어떻게 처리할 겁니까?
모든 열을 볼 수 있도록 테이블을 수평으로 스크롤해야 합니다.책상은 예민해져야 한다.
본고에서 우리는 Shopify’s Polaris UI toolkit에서 사용한 측면 스크롤 소부품(현재 React에 내장되어 있음)을 소개할 것이다. 우리는 Stimulus을 사용하여 이 기능을 다시 만들 것이다. React에서 데이터 테이블을 다시 쓸 필요가 없다.
우리는 IntersectionObserver API 을 사용할 것이다. 이것은 React 구성 요소가 최초로 사용한 것처럼 크기 조정 관찰 프로그램과 스크롤 관찰 프로그램을 추가하는 것이 아니라, 광범위하게 사용되는 새로운 브라우저 기능이다.
자극 빠른 시작
Stimulus 기존 HTML에 점진적인 상호 작용을 추가할 수 있는 애플릿 라이브러리
CSS가 요소가 문서 객체 모델(DOM)에 나타날 때 요소에 스타일을 추가하는 것처럼 Stimulus도 요소가 DOM에 나타날 때 요소에 상호작용(이벤트 프로세서, 동작 등)을 추가하고 요소가 DOM에서 제거될 때 제거합니다.Rails와 서버에서 보여 주는 HTML과 매우 일치하기 때문에 이 곳에서 사용할 것입니다.
HTML에 CSS 클래스를 추가해서 스타일을 연결할 수 있는 것처럼, 요소에 특수한 자극
data-
속성을 추가해서 상호작용을 연결할 수도 있습니다.자극은 이런 것들을 감시하고 일치할 때 상호작용을 불러일으킨다. (((table-scroll
라는 자극 컨트롤러)<div data-controller="table-scroll">
<button
class="button button-scroll-right"
data-table-scroll-target="scrollRightButton"
data-action="table-scroll#scrollRight"
>
...
</button>
</div>
Shopify Polaris 데이터 테이블에서 스크롤 탐색 다시 만들기
Shopify의 UI 라이브러리는 상하문이 수용할 수 있는 열을 초과할 때만 표시할 수 있는 똑똑한 사이드 스크롤 내비게이션 위젯을 도입했다.좌우로 스크롤하는 단추와 보기에 몇 열의 작은 점이 있는지 표시합니다.
원작이 React에 있을 때, 우리는 자극을 사용하여 재창조 기능을 사용할 것이다.이 HTML은 Shopify의 구현에서 나온 것입니다. 모든 Polaris 종류를 제거하면 응용 프로그램 스타일에 맞는 구조를 가지게 됩니다.
따라서 응용 프로그램에서 인코딩될 전체적인 태그 구조를 만들고
table-scroll
자극 컨트롤러를 추가합니다.(간결하게 보기 위해 일부 CSS 스타일이 생략되었으므로 가능한 경우 키 클래스를 호출하려고 합니다.)
<div data-controller="table-scroll">
<div data-table-scroll-target="navBar">
<!-- Navigation widget -->
</div>
<div class="flex flex-col mx-auto">
<div class="overflow-x-auto" data-table-scroll-target="scrollArea">
<table class="min-w-full">
<!-- Table contents -->
</table>
</div>
</div>
</div>
다음은 <th>
표시에 속성을 추가해서 각 열의 목표를 설정합니다.우리는 모든 열을 column
의 목표값으로 설정하여 자극적인 다목적 귀속을 이용할 수 있다. 이것은 자극 컨트롤러에서 자동으로 columnTargets
수 그룹을 귀속시킬 수 있다.<!-- Table contents -->
<table class="min-w-full">
<thead>
<tr>
<th data-table-scroll-target="column">Product</th>
<th data-table-scroll-target="column">Price</th>
<th data-table-scroll-target="column">SKU</th>
<th data-table-scroll-target="column">Sold</th>
<th data-table-scroll-target="column">Net Sales</th>
</tr>
</thead>
<tbody>
<!-- Table body -->
</tbody>
</table>
다음은 내비게이션 위젯에 표시를 만듭니다.각 열에 대해 점 아이콘을 사용하고 테이블을 스크롤하려면 왼쪽 및 오른쪽 화살표를 사용합니다.<!-- Navigation widget -->
<div data-table-scroll-target="navBar">
<!-- Left button -->
<button data-table-scroll-target="leftButton" data-action="table-scroll#scrollLeft">
<svg></svg>
</button>
<!-- Column visibility dots -->
<% 5.times do %>
<span class="text-gray-200" data-table-scroll-target="columnVisibilityIndicator">
<svg></svg>
</span>
<% end %>
<!-- Scroll Right button -->
<button data-table-scroll-target="rightButton" data-action="table-scroll#scrollRight">
<svg></svg>
</button>
</div>
마지막으로, 내비게이션 위젯을 언제 표시하거나 숨길지, 단추와 점의 스타일을 설정하는지, CSS 스타일을 정의하기 위해 클래스 데이터를 전송합니다.이러한 종류를 자극 컨트롤러에 하드코딩할 수 있지만, 항목의 필요에 따라 설정하기를 원할 수도 있습니다. 예를 들어, 이 컨트롤러를 여러 테이블과 함께 사용하기를 원할 수도 있지만, 다른 색으로 보이는 열을 표시할 수도 있습니다.<div
data-controller="table-scroll"
data-table-scroll-nav-shown-class="flex"
data-table-scroll-nav-hidden-class="hidden"
data-table-scroll-button-disabled-class="text-gray-200"
data-table-scroll-indicator-visible-class="text-blue-600"
>
<!-- The rest of the markup -->
</div>
IntersectionObserver를 사용하여 활성화
현재 우리는 이미 표시를 주석했으니, 우리는 자극 컨트롤러를 추가할 수 있다.
우리는
scrollArea
의 위치를 관찰하고 보이는 것을 검출하는 방법이 필요하다.Polaris 구현과 달리 APIIntersectionObserver
를 사용합니다.window.resize
또는 window.scroll
가 필요하지 않으며, 이는 새 네이티브 브라우저 API보다 성능이 더 비쌉니다.IntersectionObserver
API는 요소의 가시성을 모니터링하고 가시성 변경 시 콜백을 트리거합니다.우리의 예에서, 우리는 열 제목의 가시성에 주목할 것이다.// controllers/table_scroll_controller.js
import { Controller } from "stimulus";
export default class extends Controller {
static targets = [
"navBar",
"scrollArea",
"column",
"leftButton",
"rightButton",
"columnVisibilityIndicator",
];
static classes = [
"navShown",
"navHidden",
"buttonDisabled",
"indicatorVisible",
];
connect() {
// start watching the scrollAreaTarget via IntersectionObserver
}
disconnect() {
// stop watching the scrollAreaTarget, teardown event handlers
}
}
페이지의 자극성을 점차적으로 강화하고 있기 때문에, 브라우저가 지원하는지 IntersectionObserver
확인해야 하며, 지원하지 않으면 우아하게 등급을 내려야 한다.컨트롤러가 연결될 때, 우리는
IntersectionObserver
를 만들고, 리셋을 제공한 다음, 우리가 관찰하고자 하는 모든 것을 등록합니다 IntersectionObserver
.시작할 때마다
columnTargets
리셋을 시작할 때 (intersection Observer가 초기화될 때 기본적으로 시작합니다.) 모든 열 제목의 updateScrollNavigation
속성을 업데이트해서 다른 리셋을 나중에 검사할 수 있도록 합니다.import { Controller } from "stimulus";
function supportsIntersectionObserver() {
return (
"IntersectionObserver" in window ||
"IntersectionObserverEntry" in window ||
"intersectionRatio" in window.IntersectionObserverEntry.prototype
);
}
export default class extends Controller {
static targets = [...];
static classes = [...];
connect() {
this.startObservingColumnVisibility();
}
startObservingColumnVisibility() {
if (!supportsIntersectionObserver()) {
console.warn(`This browser doesn't support IntersectionObserver`);
return;
}
this.intersectionObserver = new IntersectionObserver(
this.updateScrollNavigation.bind(this),
{
root: this.scrollAreaTarget,
threshold: 0.99, // otherwise, the right-most column sometimes won't be considered visible in some browsers, rounding errors, etc.
}
);
this.columnTargets.forEach((headingEl) => {
this.intersectionObserver.observe(headingEl);
});
}
updateScrollNavigation(observerRecords) {
observerRecords.forEach((record) => {
record.target.dataset.isVisible = record.isIntersecting;
});
this.toggleScrollNavigationVisibility();
this.updateColumnVisibilityIndicators();
this.updateLeftRightButtonAffordance();
}
disconnect() {
this.stopObservingColumnVisibility();
}
stopObservingColumnVisibility() {
if (this.intersectionObserver) {
this.intersectionObserver.disconnect();
}
}
일부 코드는 내용을 설정하고 등록할 수 있지만, 이것은 상당히 간단하다. 여기서부터 나머지 작업은 열의 가시성과 내비게이션 소부품을 동기화하는 것이다.화면에서 CSS 클래스를 열고 닫으려면 Stimulus의 대상 바인딩을 사용합니다.CSS 클래스를 구성할 수 있으므로 JavaScript 패키지를 재구성하는 대신 HTML을 편집하여 UI를 조정할 수 있습니다.
toggleScrollNavigationVisibility() {
const allColumnsVisible =
this.columnTargets.length > 0 &&
this.columnTargets[0].dataset.isVisible === "true" &&
this.columnTargets[this.columnTargets.length - 1].dataset.isVisible ===
"true";
if (allColumnsVisible) {
this.navBarTarget.classList.remove(this.navShownClass);
this.navBarTarget.classList.add(this.navHiddenClass);
} else {
this.navBarTarget.classList.add(this.navShownClass);
this.navBarTarget.classList.remove(this.navHiddenClass);
}
}
updateColumnVisibilityIndicators() {
this.columnTargets.forEach((headingEl, index) => {
const indicator = this.columnVisibilityIndicatorTargets[index];
if (indicator) {
indicator.classList.toggle(
this.indicatorVisibleClass,
headingEl.dataset.isVisible === "true"
);
}
});
}
updateLeftRightButtonAffordance() {
const firstColumnHeading = this.columnTargets[0];
const lastColumnHeading = this.columnTargets[this.columnTargets.length - 1];
this.updateButtonAffordance(
this.leftButtonTarget,
firstColumnHeading.dataset.isVisible === "true"
);
this.updateButtonAffordance(
this.rightButtonTarget,
lastColumnHeading.dataset.isVisible === "true"
);
}
updateButtonAffordance(button, isDisabled) {
if (isDisabled) {
button.setAttribute("disabled", "");
button.classList.add(this.buttonDisabledClass);
} else {
button.removeAttribute("disabled");
button.classList.remove(this.buttonDisabledClass);
}
}
마지막으로 내비게이션 단추를 눌렀을 때 터치하는 동작을 추가해야 합니다.단추를 눌렀을 때, 우리는 스크롤 방향에서 다음 보이지 않는 열을 찾은 다음, 표를 스크롤해서 열의 앞부분에 놓을 것입니다.scrollLeft() {
// scroll to make visible the first non-fully-visible column to the left of the scroll area
let columnToScrollTo = null;
for (let i = 0; i < this.columnTargets.length; i++) {
const column = this.columnTargets[i];
if (columnToScrollTo !== null && column.dataset.isVisible === "true") {
break;
}
if (column.dataset.isVisible === "false") {
columnToScrollTo = column;
}
}
this.scrollAreaTarget.scroll(columnToScrollTo.offsetLeft, 0);
}
scrollRight() {
// scroll to make visible the first non-fully-visible column to the right of the scroll area
let columnToScrollTo = null;
for (let i = this.columnTargets.length - 1; i >= 0; i--) {
// right to left
const column = this.columnTargets[i];
if (columnToScrollTo !== null && column.dataset.isVisible === "true") {
break;
}
if (column.dataset.isVisible === "false") {
columnToScrollTo = column;
}
}
this.scrollAreaTarget.scroll(columnToScrollTo.offsetLeft, 0);
}
전체 코드via this gist를 보거나 대화식 예제via this Codepen를 사용할 수 있습니다.그것을 싸라
봐라!우리는 매우 아름다운 응답 스크롤 테이블을 가지고 있다.큰 화면에서, 그것은 일반적인 HTML 표처럼 보인다.그러나 뷰 포트를 축소하면 내비게이션 위젯이 나타나고 테이블의 어떤 부분이 보이는지 볼 수 있습니다.
전반적으로 말하자면, 이 컨트롤러의 코드는 200줄도 안 되므로, 전체 응용 프로그램에서 각종 크기의 표를 처리할 수 있어야 한다.
Hotwire의 발표에 따라 비수욕 응용 프로그램의'마지막 마일'상호작용에 있어 자극은 중요한 구성 부분이다.Stimum 은 대개 JavaScript 를 적게 실행하는 데 사용되지만, 미러링 기능이 완비된 UI 라이브러리를 위한 보다 견고한 컨트롤러를 구축할 수 있습니다.
특이한 클라이언트 프레임워크를 사용하기 위해 응용 프로그램 체계 구조를 완전히 바꾸기 전에 기존의 HTML 태그와 자극을 사용할 수 있는지 확인하십시오.
Reference
이 문제에 관하여(Magic Responsive Tables, 자극 및 IntersectionObserver), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/swanson/magic-responsive-tables-with-stimulus-and-intersectionobserver-21kk텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)