{"id":13725957,"url":"https://github.com/ridi/react-viewer","last_synced_at":"2025-12-24T19:53:48.541Z","repository":{"id":23116830,"uuid":"98161945","full_name":"ridi/react-viewer","owner":"ridi","description":"Online EPUB/Comics viewer","archived":false,"fork":false,"pushed_at":"2024-01-23T10:11:15.000Z","size":46338,"stargazers_count":63,"open_issues_count":63,"forks_count":9,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-11-02T07:08:01.939Z","etag":null,"topics":["comics","ebook","epub","js","react","reader","redux","viewer"],"latest_commit_sha":null,"homepage":"https://ridi.github.io/react-viewer/demo/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ridi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-24T07:27:30.000Z","updated_at":"2024-07-31T09:31:40.000Z","dependencies_parsed_at":"2024-01-23T11:39:42.942Z","dependency_job_id":null,"html_url":"https://github.com/ridi/react-viewer","commit_stats":{"total_commits":449,"total_committers":15,"mean_commits":"29.933333333333334","dds":0.3585746102449888,"last_synced_commit":"03c590201684d7bb8ee252ab9e4545ff92ce51b9"},"previous_names":["ridi/react-webviewer"],"tags_count":78,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ridi%2Freact-viewer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ridi%2Freact-viewer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ridi%2Freact-viewer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ridi%2Freact-viewer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ridi","download_url":"https://codeload.github.com/ridi/react-viewer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224654057,"owners_count":17347662,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["comics","ebook","epub","js","react","reader","redux","viewer"],"created_at":"2024-08-03T01:02:43.449Z","updated_at":"2025-12-24T19:53:48.535Z","avatar_url":"https://github.com/ridi.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# @ridi/react-viewer\n\n[![Build Status](https://travis-ci.com/ridi/react-viewer.svg?branch=master)](https://travis-ci.com/ridi/react-viewer)\n[![npm version](https://img.shields.io/npm/v/@ridi/react-viewer.svg)](https://www.npmjs.com/package/@ridi/react-viewer)\n\n## Demo\nhttps://ridi.github.io/react-viewer/demo/\n\n## Installation\n```\nnpm install @ridi/react-viewer\n```\n\n## How to Use\n\n### Initialize\n\nAdd `@ridi/react-viewer` reducer into your reducers.\n```js\nimport { reducers as reader } from '@ridi/react-viewer';\nimport { combineReducers } from 'redux';\n\nconst appReducer = combineReducers({\n    ...\n    reader,\n    ...\n});\n```\n\nConnect `Connector` with redux store.\n```js\nimport { createStore } from 'redux';\nimport { Connector } from '@ridi/react-viewer';\n\nconst store = createStore( ... );\nConnector.connect(store);\n```\n\n### Quick start\n\n`Service` must be loaded for initializing Reader's lifecycle.\n\nAnd put `Reader` component into your component.\n\n```js\nimport React from 'react';\nimport Reader, { Service } from '@ridi/react-viewer';\n\nService.loadAll();\nexport default ViewerPage extends React.Component {\n    render() {\n        return \u003cReader /\u003e;\n    }\n};\n```\n\n#### `Service`\n\n- `loadAll`\n  - params:\n    - `restoreState`(`Object`): state object for restoring redux store\n    - `config`(`Object`)\n      - `beforeContentCalculated`: Check out [Hooks](#hooks) section for more details\n\n#### `\u003cReader\u003e` Component\n\n`Reader` component provides all functionality of viewer and renders viewer body.\n\nHere are `Reader`'s properties:\n\n* `footer`(node): markup for the footer area\n* `contentFooter`(node): markup for the content footer area\n* `selectable`(boolean): set reader to be selectable\n* `annotationable`(boolean): set reader to be annotationable\n* `annotations`(array): annotation list is composed of items that has distinct `id` property. \n\n\n\n### Events\n\nBelow events can be used with `EventBus` \n\n```js\nimport { EventBus, Events, TouchEvent } from '@ridi/react-viewer';\n\nEventBus.on(Events.TOUCH, (event) =\u003e {\n  const { clientX, clientY, annotation } = event.detail;\n  if (event.type === TouchEvent.TouchAnnotation) {\n    console.log(annotation);\n  } else {\n    console.log(clientX, clientY);\n  }\n});\n\nEventBus.emit(Events.SET_CONTENTS_BY_URI, { ... });\n```\n\n* `Events.SET_CONTENTS_BY_URI` (emmitable): Check out [Render Contents](#render-contents) section for more details\n  - params:\n    - `data`(`Object`)\n      - `contentFormat`(`ContentFormat`)\n      - `bindingType`(`BindingType`)\n      - `uris`(`Array`): Array of uri to fetch content\n* `Events.SET_CONTENTS_BY_VALUE` (emmitable): Check out [Render Contents](#render-contents) section for more details\n  - params:\n    - `data`(`Object`)\n      - `contentFormat`(`ContentFormat`)\n      - `bindingType`(`BindingType`)\n      - `contents`(`Array`): Array of HTML document(`contentFormat` === `ContentFormat.HTML`) or base64 encoded image source(`contentFormat` === `ContentFormat.IMAGE`)\n* `Events.SCROLL`(listenable): Screen is scrolled\n  - params:\n    - `event`(`ScrollEvent`)\n* `Events.TOUCH`(listenable): Screen is touched (or annotation is touched)\n  - params:\n    - `event`(`TouchEvent`)\n      - `type`(`TouchEvent`): `TouchEvent.Touch` or `TouchEvent.AnnotationTouch`\n      - `detail`\n        - `screenX`\n        - `screenY`\n        - `clientX`\n        - `clientY`\n        - `pageX`\n        - `pageY`\n        - `type`: original event type\n        - `target`: original event target\n        - `annotation`: annotation info\n* `Events.CHANGE_SELECTION`(listenable): Current selection is changed\n  - params:\n    - `event`(`Object`)\n      - `selection`: selection info\n      - `selectionMode`(`SelectionMode`)\n* `Events.MOUNTED`(listenable): `\u003cReader\u003e` has been mounted\n* `Events.UNMOUNTED`(listenable): `\u003cReader\u003e` has been unmounted\n\n### Hooks\n\nYou would use *hooks* when you want to intercept some point of reader's lifecycle.\nHook implementations can return value in any forms compatible with [RxJS's ObservableInput](https://rxjs-dev.firebaseapp.com/api/index/type-alias/ObservableInput). \n\n* `beforeContentCalculated`: executed just right before per content calculation is completed\n  * params:\n    * `contentIndex`(number): index number of current calculating content\n    * `readerJsHelper`(`ReaderJsHelper`): `ReaderJsHelper` instance mounted on this current calculating content\n\n* `afterContentCalculated`: executed just right after per content calculation is completed\n  * params:\n    * `calculations`(Array): current status of calculations\n\n### Render Contents\n\n#### `Events.SET_CONTENTS_BY_URI` or `Events.SET_CONTENTS_BY_VALUE`\n\nWhole contents with metadata must set in a time.\nEmit `Events.SET_CONTENTS_BY_(URI/VALUE)` event with URIs of contents or contents loaded already.\n\n```js\nimport {\n  ContentFormat,\n  BindingType,\n  EventBus,\n  Events,\n} from '@ridi/react-viewer';\n\nEventBus.emit(Events.SET_CONTENTS_BY_URI, {\n  contentFormat: ContentFormat.HTML,\n  bindingType: BindingType.LEFT,\n  uris: [\n    './uri1.json',\n    './uri2.json',\n    ...,\n  ],\n});\n\nEventBus.emit(Events.SET_CONTENTS_BY_VALUE, {\n  contentFormat: ContentFormat.HTML,\n  bindingType: BindingType.LEFT,\n  contents: [\n    '\u003cp\u003e...\u003c/p\u003e',\n    '\u003cp\u003e...\u003c/p\u003e',\n    ...,\n  ],\n});\n```\n\n* `contentFormat`: content format (HTML: 0, IMAGE: 1)\n* `bindingType`: binding type (LEFT: 0, RIGHT: 1)\n\n## How to Run Demo\n\n```\n$ npm install\n$ npm run install:demo\n$ npm run watch\n```\nBrowse http://localhost:8000.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fridi%2Freact-viewer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fridi%2Freact-viewer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fridi%2Freact-viewer/lists"}