Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/components-web/inhouse-components-web.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions packages/components-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 13 additions & 0 deletions packages/pagination/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Inhouse Pagination

## Usage

### Installation

```bash
$ npm install @pepabo-inhouse/pagination

# or

$ yarn add @pepabo-inhouse/pagination
```
1 change: 1 addition & 0 deletions packages/pagination/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@forward "./mixins" show style, export;
118 changes: 118 additions & 0 deletions packages/pagination/_mixins.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
@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";
}

// 端のページでは 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);
}
}
}

@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);
}
}
3 changes: 3 additions & 0 deletions packages/pagination/_variables.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$default-option: (
size: m,
);
2 changes: 2 additions & 0 deletions packages/pagination/inhouse-pagination.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@use "./mixins";
@include mixins.export;
21 changes: 21 additions & 0 deletions packages/pagination/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
111 changes: 111 additions & 0 deletions packages/stories-web/src/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { FC, HTMLAttributes } from 'react'
import { Size } from './types'

type HTMLProps = HTMLAttributes<HTMLElement>

export type Format = 'numbered' | 'simple'

export interface Props extends HTMLProps {
current: number;
max: number;
format?: Format;
siblingCount?: number;
withCounter?: boolean;
size?: Extract<Size, 's' | 'm' | 'l'>;
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: 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 (
<nav
className={classes}
aria-label={ariaLabel}
{...rest}
>
{current > 1 ? (
<a className="_previous" href={hrefTemplate(current - 1)}>前のページ</a>
) : (
<span className="_previous -is-placeholder">前のページ</span>
)}
{format === 'numbered' && (
<ul className="_list">
{getPageItems(current, max, siblingCount).map((item, index) => (
<li
key={item === 'truncation' ? `truncation-${index}` : item}
className="_item"
{...(item === 'truncation' ? { 'aria-hidden': true } : {})}
>
{item === 'truncation' ? (
<span className="_truncation">…</span>
) : item === current ? (
<span className="_current" aria-current="page">{item}</span>
) : (
<a className="_link" href={hrefTemplate(item)}>{item}</a>
)}
</li>
))}
</ul>
)}
{(format === 'simple' || withCounter) && (
<span className="_counter">{current} / {max}</span>
)}
{current < max ? (
<a className="_next" href={hrefTemplate(current + 1)}>次のページ</a>
) : (
<span className="_next -is-placeholder">次のページ</span>
)}
</nav>
);
};

export default Pagination;
51 changes: 51 additions & 0 deletions packages/stories-web/src/pagination.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = (args) => {
return (
<Pagination
ariaLabel="検索結果"
{...args}
/>
)
}

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'
}
Loading