From 07a38d1be08df0eff01bdb6b2117fef826352182 Mon Sep 17 00:00:00 2001 From: "akiko.yamakawa" Date: Tue, 7 Jul 2026 17:12:23 +0900 Subject: [PATCH 1/2] =?UTF-8?q?pagination=E3=82=B3=E3=83=B3=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 16 +++ .../inhouse-components-web.scss | 2 + packages/components-web/package.json | 1 + packages/pagination/README.md | 13 ++ packages/pagination/_index.scss | 1 + packages/pagination/_mixins.scss | 111 ++++++++++++++++++ packages/pagination/_variables.scss | 3 + packages/pagination/inhouse-pagination.scss | 2 + packages/pagination/package.json | 21 ++++ .../stories-web/src/components/Pagination.tsx | 107 +++++++++++++++++ .../stories-web/src/pagination.stories.tsx | 51 ++++++++ 11 files changed, 328 insertions(+) create mode 100644 packages/pagination/README.md create mode 100644 packages/pagination/_index.scss create mode 100644 packages/pagination/_mixins.scss create mode 100644 packages/pagination/_variables.scss create mode 100644 packages/pagination/inhouse-pagination.scss create mode 100644 packages/pagination/package.json create mode 100644 packages/stories-web/src/components/Pagination.tsx create mode 100644 packages/stories-web/src/pagination.stories.tsx diff --git a/package-lock.json b/package-lock.json index 1687430..5ec07cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4776,6 +4776,10 @@ "resolved": "packages/navigation-drawer", "link": true }, + "node_modules/@pepabo-inhouse/pagination": { + "resolved": "packages/pagination", + "link": true + }, "node_modules/@pepabo-inhouse/progress-indicator": { "resolved": "packages/progress-indicator", "link": true @@ -31250,6 +31254,7 @@ "@pepabo-inhouse/interactive-table": "^4.0.0", "@pepabo-inhouse/list": "^4.0.0", "@pepabo-inhouse/navigation-drawer": "^4.0.0", + "@pepabo-inhouse/pagination": "^4.0.0", "@pepabo-inhouse/progress-indicator": "^4.0.0", "@pepabo-inhouse/radio": "^4.0.0", "@pepabo-inhouse/scrim": "^4.0.0", @@ -31431,6 +31436,17 @@ "@pepabo-inhouse/tokens": "^2.4.0" } }, + "packages/pagination": { + "version": "4.0.0", + "dependencies": { + "@pepabo-inhouse/adapter": "^4.0.0", + "@pepabo-inhouse/icon": "^4.0.0" + }, + "devDependencies": { + "@pepabo-inhouse/flavor": "^4.0.0", + "@pepabo-inhouse/tokens": "^2.4.0" + } + }, "packages/progress-indicator": { "name": "@pepabo-inhouse/progress-indicator", "version": "4.0.0", diff --git a/packages/components-web/inhouse-components-web.scss b/packages/components-web/inhouse-components-web.scss index 583e81e..016bdf4 100644 --- a/packages/components-web/inhouse-components-web.scss +++ b/packages/components-web/inhouse-components-web.scss @@ -19,6 +19,7 @@ @use "@pepabo-inhouse/interactive-table"; @use "@pepabo-inhouse/list"; @use "@pepabo-inhouse/navigation-drawer"; +@use "@pepabo-inhouse/pagination"; @use "@pepabo-inhouse/progress-indicator"; @use "@pepabo-inhouse/radio"; @use "@pepabo-inhouse/scrim"; @@ -54,6 +55,7 @@ @include interactive-table.export; @include list.export; @include navigation-drawer.export; +@include pagination.export; @include progress-indicator.export; @include radio.export; @include scrim.export; diff --git a/packages/components-web/package.json b/packages/components-web/package.json index 4aef1db..8b0e790 100644 --- a/packages/components-web/package.json +++ b/packages/components-web/package.json @@ -34,6 +34,7 @@ "@pepabo-inhouse/interactive-table": "^4.0.0", "@pepabo-inhouse/list": "^4.0.0", "@pepabo-inhouse/navigation-drawer": "^4.0.0", + "@pepabo-inhouse/pagination": "^4.0.0", "@pepabo-inhouse/progress-indicator": "^4.0.0", "@pepabo-inhouse/radio": "^4.0.0", "@pepabo-inhouse/scrim": "^4.0.0", diff --git a/packages/pagination/README.md b/packages/pagination/README.md new file mode 100644 index 0000000..dc3ba68 --- /dev/null +++ b/packages/pagination/README.md @@ -0,0 +1,13 @@ +# Inhouse Pagination + +## Usage + +### Installation + +```bash +$ npm install @pepabo-inhouse/pagination + +# or + +$ yarn add @pepabo-inhouse/pagination +``` diff --git a/packages/pagination/_index.scss b/packages/pagination/_index.scss new file mode 100644 index 0000000..9e8069c --- /dev/null +++ b/packages/pagination/_index.scss @@ -0,0 +1 @@ +@forward "./mixins" show style, export; diff --git a/packages/pagination/_mixins.scss b/packages/pagination/_mixins.scss new file mode 100644 index 0000000..fe0b817 --- /dev/null +++ b/packages/pagination/_mixins.scss @@ -0,0 +1,111 @@ +@use "sass:map"; +@use "@pepabo-inhouse/adapter/functions" as adapter; +@use "@pepabo-inhouse/icon/mixins" as icon; +@use "./variables"; + +@mixin style($options: variables.$default-option) { + $options: map.merge(variables.$default-option, $options); + + display: flex; + gap: adapter.get-spacing-size($level: xs); + align-items: center; + justify-content: center; + + ._list { + display: flex; + flex-wrap: wrap; + gap: adapter.get-spacing-size($level: xs); + justify-content: center; + margin: 0; + padding: 0; + list-style: none; + } + + ._item { + display: inline-flex; + } + + @include -size-rule($size: map.get($options, size)); + + ._link, + ._previous, + ._next { + color: adapter.get-implication-color($name: interactive, $level: 700); + text-decoration: underline; + + &:hover { + background: adapter.get-background-overlay-color($brightness: light, $state: hover); + } + + &:active { + background: adapter.get-background-overlay-color($brightness: light, $state: active); + } + + &:focus-visible { + outline: adapter.get-focus-ring-outline(); + outline-offset: adapter.get-focus-ring-outline-offset(); + } + } + + ._current { + color: adapter.get-text-color($brightness: light, $name: high_emphasis); + font-weight: bold; + background: adapter.get-surface-color(light, secondary); + } + + ._truncation { + color: adapter.get-text-color($brightness: light, $name: low_emphasis); + } + + ._counter { + color: adapter.get-text-color($brightness: light, $name: high_emphasis); + } + + ._previous::before { + @include icon.style; + + content: "chevron_left"; + } + + ._next::after { + @include icon.style; + + content: "chevron_right"; + } + + @each $size in adapter.get-interactive-component-height-keys() { + &.-size-#{$size} { + @include -size-rule($size: $size); + } + } +} + +@mixin export { + .in-pagination { + @include style; + } +} + +@mixin -size-rule($size) { + ._link, + ._current, + ._truncation, + ._previous, + ._next { + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-width: adapter.get-interactive-component-height($level: $size); + height: adapter.get-interactive-component-height($level: $size); + padding-inline: adapter.get-spacing-size($level: xs); + font-size: adapter.get-font-size($level: $size); + line-height: adapter.get-line-height($level: $size, $density: normal); + border-radius: adapter.get-radius-size($level: m); + } + + ._counter { + font-size: adapter.get-font-size($level: $size); + line-height: adapter.get-line-height($level: $size, $density: normal); + } +} diff --git a/packages/pagination/_variables.scss b/packages/pagination/_variables.scss new file mode 100644 index 0000000..5d17522 --- /dev/null +++ b/packages/pagination/_variables.scss @@ -0,0 +1,3 @@ +$default-option: ( + size: m, +); diff --git a/packages/pagination/inhouse-pagination.scss b/packages/pagination/inhouse-pagination.scss new file mode 100644 index 0000000..b2be6e9 --- /dev/null +++ b/packages/pagination/inhouse-pagination.scss @@ -0,0 +1,2 @@ +@use "./mixins"; +@include mixins.export; diff --git a/packages/pagination/package.json b/packages/pagination/package.json new file mode 100644 index 0000000..b2df797 --- /dev/null +++ b/packages/pagination/package.json @@ -0,0 +1,21 @@ +{ + "name": "@pepabo-inhouse/pagination", + "description": "Inhouse Components for the web pagination component", + "version": "4.0.0", + "repository": { + "type": "git", + "url": "https://github.com/pepabo/inhouse-components-web.git", + "directory": "packages/pagination" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pepabo-inhouse/adapter": "^4.0.0", + "@pepabo-inhouse/icon": "^4.0.0" + }, + "devDependencies": { + "@pepabo-inhouse/flavor": "^4.0.0", + "@pepabo-inhouse/tokens": "^2.4.0" + } +} diff --git a/packages/stories-web/src/components/Pagination.tsx b/packages/stories-web/src/components/Pagination.tsx new file mode 100644 index 0000000..9ef1dda --- /dev/null +++ b/packages/stories-web/src/components/Pagination.tsx @@ -0,0 +1,107 @@ +import React, { FC, HTMLAttributes } from 'react' +import { Size } from './types' + +type HTMLProps = HTMLAttributes + +export type Format = 'numbered' | 'simple' + +export interface Props extends HTMLProps { + current: number; + max: number; + format?: Format; + siblingCount?: number; + withCounter?: boolean; + size?: Extract; + ariaLabel?: string; + hrefTemplate?: (page: number) => string; +} + +type PageItem = number | 'truncation' + +const getPageItems = (current: number, max: number, siblingCount: number): PageItem[] => { + const first = 1; + const last = max; + const start = Math.max(current - siblingCount, first + 1); + const end = Math.min(current + siblingCount, last - 1); + + const items: PageItem[] = [first]; + + if (start > first + 1) { + items.push('truncation'); + } + + for (let page = start; page <= end; page++) { + items.push(page); + } + + if (end < last - 1) { + items.push('truncation'); + } + + if (last > first) { + items.push(last); + } + + return items; +}; + +const Pagination: FC = (props: Props) => { + const { + current, + max, + format = 'numbered', + siblingCount = 2, + withCounter = false, + size, + ariaLabel, + hrefTemplate = (page: number) => `?page=${page}`, + ...rest + } = props; + + const classList = ['in-pagination']; + + if (typeof size !== 'undefined') { + classList.push(`-size-${size}`); + } + + const classes = classList.join(' '); + + return ( + + ); +}; + +export default Pagination; diff --git a/packages/stories-web/src/pagination.stories.tsx b/packages/stories-web/src/pagination.stories.tsx new file mode 100644 index 0000000..bd4af8e --- /dev/null +++ b/packages/stories-web/src/pagination.stories.tsx @@ -0,0 +1,51 @@ +import type { StoryFn, Meta } from '@storybook/react' +import React from 'react' +import Pagination, { Props } from './components/Pagination' + +export default { + title: 'Components/Pagination', + component: Pagination, + argTypes: { + hrefTemplate: { table: { disable: true } } + } +} as Meta + +const Template: StoryFn = (args) => { + return ( + + ) +} + +export const Index = Template.bind({}) +Index.args = { + current: 5, + max: 10 +} + +export const FirstPage = Template.bind({}) +FirstPage.args = { + current: 1, + max: 10 +} + +export const LastPage = Template.bind({}) +LastPage.args = { + current: 10, + max: 10 +} + +export const FewPages = Template.bind({}) +FewPages.args = { + current: 2, + max: 3 +} + +export const Simple = Template.bind({}) +Simple.args = { + current: 5, + max: 10, + format: 'simple' +} From c9f2ba235c4ab81a6f3393e09348b20edae28ede Mon Sep 17 00:00:00 2001 From: "akiko.yamakawa" Date: Fri, 10 Jul 2026 10:02:57 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E7=AB=AF=E3=81=AE=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=81=A7=20Previous=20/=20Next=20=E3=81=AE=E9=A0=98?= =?UTF-8?q?=E5=9F=9F=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ドキュメント(design/design.pepabo.com#632 の States > Disabled 節)の 「出力しない場合も、要素分の領域は保持します」に実装を合わせる。 - 端のページではリンクの代わりに ._previous / ._next の -is-placeholder(visibility: hidden の span)を出力し、 レイアウトシフトと連続操作中の誤クリックを防ぐ - プレースホルダーは同一ラベル・同一基底クラスのため幅が一致する --- packages/pagination/_mixins.scss | 7 +++++++ packages/stories-web/src/components/Pagination.tsx | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/pagination/_mixins.scss b/packages/pagination/_mixins.scss index fe0b817..17d67d1 100644 --- a/packages/pagination/_mixins.scss +++ b/packages/pagination/_mixins.scss @@ -73,6 +73,13 @@ content: "chevron_right"; } + // 端のページでは Previous / Next を出力しない代わりにプレースホルダーを置き、 + // 要素分の領域を保持する(レイアウトシフトと連続操作中の誤クリックを防ぐ) + ._previous.-is-placeholder, + ._next.-is-placeholder { + visibility: hidden; + } + @each $size in adapter.get-interactive-component-height-keys() { &.-size-#{$size} { @include -size-rule($size: $size); diff --git a/packages/stories-web/src/components/Pagination.tsx b/packages/stories-web/src/components/Pagination.tsx index 9ef1dda..29f27ff 100644 --- a/packages/stories-web/src/components/Pagination.tsx +++ b/packages/stories-web/src/components/Pagination.tsx @@ -72,8 +72,10 @@ const Pagination: FC = (props: Props) => { aria-label={ariaLabel} {...rest} > - {current > 1 && ( + {current > 1 ? ( 前のページ + ) : ( + 前のページ )} {format === 'numbered' && (
    @@ -97,8 +99,10 @@ const Pagination: FC = (props: Props) => { {(format === 'simple' || withCounter) && ( {current} / {max} )} - {current < max && ( + {current < max ? ( 次のページ + ) : ( + 次のページ )} );