generated from gravity-ui/package-example
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Stories): add unstable versions of Stories and StoriesGroup comp…
…onents (#245)
- Loading branch information
1 parent
5f3a336
commit 8ac2cdf
Showing
34 changed files
with
1,885 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
## Stories | ||
|
||
Component for displaying stories. It looks like a carousel in a modal with given places to display text and media. | ||
|
||
### PropTypes | ||
|
||
| Property | Type | Required | Default | Description | | ||
| :------------------ | :-------------- | :------- | :------ | :----------------------------------------------- | | ||
| open | `Boolean` | ✓ | | Visibility flag | | ||
| items | `StoriesItem[]` | ✓ | | List of stories to display | | ||
| initialStoryIndex | `Number` | | 0 | Index of the first story to be displayed | | ||
| onClose | `Function` | | | Action on close | | ||
| onPreviousClick | `Function` | | | Action when switching to previous story | | ||
| onNextClick | `Function` | | | Action when switching to next story | | ||
| disableOutsideClick | `Boolean` | | true | If `true`, do not close stories on click outside | | ||
| className | `string` | | | Stories modal class | | ||
|
||
### StoriesItem object | ||
|
||
| Field | Type | Required | Default | Description | | ||
| --------------- | ------------------------ | -------- | ------- | ---------------------------------------- | | ||
| title | `String` | | | Title | | ||
| description | `String` | | | Main text | | ||
| url | `String` | | | Link to display more information | | ||
| media | `StoriesItemMedia` | | | Media content | | ||
| firstAction | `ButtonProps` | | | Custom action button props | | ||
| secondAction | `ButtonProps` | | | Custom action button props | | ||
| textBlockStyle | `StoriesTextBlockStyle` | ✓ | | Props for styling text content in Story | | ||
| mediaBlockStyle | `StoriesMediaBlockStyle` | ✓ | | Props for styling media content in Story | | ||
| textColorStyles | `StoriesItemTextStyles` | | | Props for styling text color in Story | | ||
|
||
### StoriesItemMedia object | ||
|
||
| Field | Type | Required | Default | Description | | ||
| --------- | -------- | -------- | ------- | -------------------------------------------------- | | ||
| type | `String` | ✓ | | Content type (`image` or `video`) | | ||
| url | `String` | ✓ | | File link | | ||
| url2x | `String` | | | File link for Retina display (only used for image) | | ||
| posterUrl | `String` | | | Poster URL (only used for video) | | ||
|
||
### StoriesItemTextStyles object | ||
|
||
| Field | Type | Required | Default | Description | | ||
| ---------------- | -------- | -------- | ------- | -------------------------------- | | ||
| titleColor | `String` | | | Apply color to Story title | | ||
| descriptionColor | `String` | | | Apply color to Story description | | ||
| counterColor | `String` | | | Apply color to Story counter | | ||
|
||
#### Usage example | ||
|
||
```jsx harmony | ||
<Stories | ||
open | ||
items={[ | ||
{ | ||
title: 'Story title', | ||
description: 'Story text', | ||
type: 'image', | ||
media: { | ||
url: 'https://storage.yandexcloud.net/uikit-storybook-assets/story-picture-2.png', | ||
}, | ||
}, | ||
]} | ||
/> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
@use '../variables'; | ||
|
||
$block: '.#{variables.$ns}stories'; | ||
|
||
#{$block} { | ||
--g-modal-border-radius: var(--g-spacing-5); | ||
--g-modal-margin: var(--g-spacing-5); | ||
|
||
& .g-modal__content-wrapper { | ||
overflow-x: initial; | ||
} | ||
|
||
&__modal-content { | ||
border-radius: var(--g-spacing-5); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import React from 'react'; | ||
|
||
import type {ModalCloseReason} from '@gravity-ui/uikit'; | ||
import {Modal} from '@gravity-ui/uikit'; | ||
|
||
import {block} from '../utils/cn'; | ||
|
||
import {IndexType, StoriesLayout} from './components/StoriesLayout/StoriesLayout'; | ||
import {useSyncWithLS} from './hooks'; | ||
import type {StoriesItem} from './types'; | ||
|
||
import './Stories.scss'; | ||
|
||
const b = block('stories'); | ||
|
||
export interface StoriesProps { | ||
open: boolean; | ||
items: StoriesItem[]; | ||
onClose?: ( | ||
event: MouseEvent | KeyboardEvent | React.MouseEvent<HTMLElement, MouseEvent>, | ||
reason: ModalCloseReason | 'closeButtonClick', | ||
) => void; | ||
initialStoryIndex?: number; | ||
onPreviousClick?: (storyIndex: number) => void; | ||
onNextClick?: (storyIndex: number) => void; | ||
disableOutsideClick?: boolean; | ||
className?: string; | ||
syncInTabsKey?: string; | ||
} | ||
|
||
export function Stories({ | ||
open, | ||
onClose, | ||
items, | ||
onPreviousClick, | ||
onNextClick, | ||
initialStoryIndex = 0, | ||
disableOutsideClick = true, | ||
className, | ||
syncInTabsKey, | ||
}: StoriesProps) { | ||
const [storyIndex, setStoryIndex] = React.useState(initialStoryIndex); | ||
|
||
const handleClose = React.useCallback<NonNullable<StoriesProps['onClose']>>( | ||
(event, reason) => { | ||
onClose?.(event, reason); | ||
}, | ||
[onClose], | ||
); | ||
|
||
const {callback: closeWithLS} = useSyncWithLS<NonNullable<StoriesProps['onClose']>>({ | ||
callback: (event, reason) => { | ||
onClose?.(event, reason); | ||
}, | ||
uniqueKey: `close-story-${syncInTabsKey}`, | ||
}); | ||
|
||
const handleButtonClose = React.useCallback< | ||
(event: MouseEvent | KeyboardEvent | React.MouseEvent<HTMLElement, MouseEvent>) => void | ||
>( | ||
(event) => { | ||
handleClose(event, 'closeButtonClick'); | ||
if (syncInTabsKey) closeWithLS(event, 'closeButtonClick'); | ||
}, | ||
[handleClose, syncInTabsKey, closeWithLS], | ||
); | ||
|
||
const handleGotoPrevious = React.useCallback(() => { | ||
setStoryIndex((currentStoryIndex) => { | ||
if (currentStoryIndex <= 0) { | ||
return 0; | ||
} | ||
|
||
const newIndex = currentStoryIndex - 1; | ||
onPreviousClick?.(newIndex); | ||
return newIndex; | ||
}); | ||
}, [onPreviousClick]); | ||
|
||
const handleGotoNext = React.useCallback(() => { | ||
setStoryIndex((currentStoryIndex) => { | ||
if (currentStoryIndex >= items.length - 1) { | ||
return items.length - 1; | ||
} | ||
|
||
const newIndex = currentStoryIndex + 1; | ||
onNextClick?.(newIndex); | ||
return newIndex; | ||
}); | ||
}, [items, onNextClick]); | ||
|
||
if (items.length === 0) { | ||
return null; | ||
} | ||
|
||
// case when items has changed and index has ceased to be valid | ||
if (items[storyIndex] === undefined) { | ||
const correctIndex = items[initialStoryIndex] === undefined ? 0 : initialStoryIndex; | ||
setStoryIndex(correctIndex); | ||
|
||
return null; | ||
} | ||
|
||
const indexType = | ||
(items.length === 1 && IndexType.Single) || | ||
(storyIndex === 0 && IndexType.Start) || | ||
(storyIndex === items.length - 1 && IndexType.End) || | ||
IndexType.InProccess; | ||
|
||
return ( | ||
<Modal | ||
open={open} | ||
onClose={handleClose} | ||
disableOutsideClick={disableOutsideClick} | ||
className={b()} | ||
contentClassName={b('modal-content', className)} | ||
> | ||
<StoriesLayout | ||
items={items} | ||
storyIndex={storyIndex} | ||
indexType={indexType} | ||
handleButtonClose={handleButtonClose} | ||
handleGotoNext={handleGotoNext} | ||
handleGotoPrevious={handleGotoPrevious} | ||
/> | ||
</Modal> | ||
); | ||
} |
Oops, something went wrong.