├── .env.dist ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── bin └── build ├── composer.json ├── composer.lock ├── config ├── berti.config.php ├── webpack-dev-server.config.js └── webpack.config.js ├── data ├── articles.php ├── built_with.php ├── components.php └── talks.php ├── package-lock.json ├── package.json ├── pages ├── changelog.md └── index.md ├── src ├── Twig │ └── Extension.php ├── berti.php └── data.php ├── static-files ├── CNAME ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── assets │ ├── 139.3ced3f7b.css │ ├── 139.820be3d5.js │ ├── 261.bd162b4a.js │ ├── 261.bd162b4a.js.LICENSE.txt │ ├── 553.90d4e0e8.css │ ├── 553.da065a6b.js │ ├── 67.4ae9feba.css │ ├── 67.ab055693.js │ ├── icons.b4abbede.svg │ ├── icons.cf61dfff.woff │ ├── icons.f41c5641.ttf │ ├── main.09dbcb8e.css │ ├── main.93d54642.js │ ├── manifest.json │ ├── runtime.124568e7.js │ ├── search.17d09555.svg │ ├── sourcesanspro-bold.cb7d3610.woff │ └── sourcesanspro-regular.f84b2bd4.woff ├── browserconfig.xml ├── favicon-32x32.png ├── favicon.ico ├── manifest.json ├── mstile-150x150.png ├── og-image.png ├── robots.txt └── safari-pinned-tab.svg └── theme ├── assets ├── components │ ├── component-info │ │ ├── index.js │ │ └── style.css │ ├── docsearch │ │ ├── algolia-autocomplete.css │ │ ├── algolia-autocomplete.js │ │ └── index.js │ ├── off-canvas-menu │ │ ├── index.css │ │ ├── index.js │ │ ├── menu.css │ │ └── menu.js │ └── version-selector │ │ ├── index.css │ │ ├── index.js │ │ ├── selector.css │ │ └── selector.js ├── fonts │ ├── fonts.css │ ├── sourcesanspro-bold.woff │ └── sourcesanspro-regular.woff ├── icons │ ├── fonts │ │ ├── icons.svg │ │ ├── icons.ttf │ │ └── icons.woff │ ├── icons.css │ ├── selection.json │ └── style.css ├── img │ ├── search.png │ └── search.svg ├── index.js ├── styles │ ├── alert.css │ ├── anchor.css │ ├── base.css │ ├── box.css │ ├── center.css │ ├── code.css │ ├── footer.css │ ├── grid.css │ ├── hamburger.css │ ├── header.css │ ├── highlight.css │ ├── iframe.css │ ├── links.css │ ├── searchbox.css │ ├── styles.css │ ├── toc.css │ ├── variables.css │ └── welcome.css └── webpack-public-path.js ├── changelog.html.twig ├── component-changelog.html.twig ├── component-license.html.twig ├── component.html.twig ├── default.html.twig ├── homepage.html.twig └── partials ├── changelog.html.twig └── header_menu.html.twig /.env.dist: -------------------------------------------------------------------------------- 1 | GITHUB_TOKEN="" 2 | DEPLOY_REPO="reactphp/reactphp.github.io" 3 | DEPLOY_TARGET_BRANCH="master" 4 | DEPLOY_URL="https://reactphp.org" 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | Deploy: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: shivammathur/setup-php@v2 14 | with: 15 | php-version: 7.1 16 | - run: composer install 17 | - run: mkdir ~/.ssh && echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_rsa && chmod 400 ~/.ssh/id_rsa 18 | - run: echo 'GITHUB_TOKEN="${{ secrets.PAT || secrets.GITHUB_TOKEN }}"' > .env && cat .env.dist >> .env 19 | - run: git config --global user.name "GitHub Actions" && git config --global user.email "actions@github.com" 20 | - run: bin/build 21 | - run: bin/build --deploy --no-component-update 22 | if: ${{ github.ref == 'refs/heads/main' }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /tmp 3 | /vendor 4 | /.env 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Jan Sorgalla 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | [![CI status](https://github.com/reactphp/website/actions/workflows/ci.yml/badge.svg)](https://github.com/reactphp/website/actions) 4 | [![Last deployed](https://img.shields.io/github/last-commit/reactphp/reactphp.github.io?label=last%20deployed&logo=github)](https://github.com/reactphp/reactphp.github.io) 5 | 6 | Source code of reactphp.org. 7 | 8 | ## Setup 9 | 10 | 1. Copy `.env.dist` to `.env` and add a 11 | [personal access token](https://github.com/settings/tokens) to the 12 | `GITHUB_TOKEN` entry. 13 | 14 | You don't need to check any of the scopes, `public access` is enough. The 15 | build script uses the GitHub API to render markdown files and fetch 16 | repository data and using the access token ensures that you don't run into 17 | API rate limits. 18 | 19 | 2. Install dependencies with `composer install`. 20 | 21 | ## Build 22 | 23 | Once set up, you can build the website by executing this: 24 | 25 | ```bash 26 | bin/build 27 | ``` 28 | 29 | This script will fetch all project repositories and then rebuild the entire website. 30 | The resulting static website will be built into the `tmp/build` directory. 31 | 32 | If you're working on the website source code, you may want to skip fetching all 33 | components on every build like this: 34 | 35 | ```bash 36 | bin/build --no-component-update 37 | ``` 38 | 39 | If you're working on the website CSS or Javascript code, you will have to 40 | rebuild the static assets like this: 41 | 42 | ```bash 43 | npm run-script build 44 | ``` 45 | 46 | > Note that compiled assets are expected to change much less frequently and are 47 | under version control. Run `npm install` to install and later commit any changes 48 | in `static-files/assets/`. 49 | 50 | ## Deploy 51 | 52 | Once built (see previous chapter), deployment is as simple as hosting the static 53 | website contents of the `tmp/build` directory behind a webserver of your choice. 54 | 55 | We use GitHub Pages to deploy this to the live site. This is done by pushing the 56 | contents of the `tmp/build` directory to the repository hosted in 57 | [reactphp/reactphp.github.io](https://github.com/reactphp/reactphp.github.io). 58 | 59 | This deployment can be started by executing this: 60 | 61 | ```bash 62 | bin/build --deploy 63 | ``` 64 | 65 | Note that this will publish any changes you've made to your local repository, 66 | including any uncommitted ones. There should usually be no need to do this 67 | manually, see next chapter for auto-deployment. 68 | 69 | ## Auto-Deployment 70 | 71 | The website can be automatically deployed via the GitHub Pages feature. 72 | 73 | Any time a commit is merged (such as when a PR is merged), GitHub actions will 74 | automatically build and deploy the website. This is done by running the above 75 | deployment script (see previous chapter). 76 | 77 | > Repository setup: 78 | > We're using a SSH deploy key for pushing to this target repository. 79 | > Make sure the required `DEPLOY_KEY` secret is set in the repository settings on GitHub. 80 | > See [action documentation](https://github.com/JamesIves/github-pages-deploy-action#using-an-ssh-deploy-key-) 81 | > for more details. 82 | > On top of this, you're recommended to add a [personal access token](https://github.com/settings/tokens) 83 | > as a repository secret with the name `PAT` to avoid running into secondary rate limits. 84 | > If this secret is not found, it will fall back to the automatic `GITHUB_TOKEN` 85 | > secret, which may cause the build to fail occasionally. 86 | 87 | ## License 88 | 89 | Released under the [MIT](LICENSE) license. 90 | 91 | **Note:** The logo and the brand name are not MIT licensed. 92 | Please check their [LICENSE](https://github.com/reactphp/branding/blob/master/LICENSE) 93 | for usage guidelines. 94 | -------------------------------------------------------------------------------- /bin/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | mustRun(function ($type, $buffer) { 21 | echo $buffer; 22 | }); 23 | } 24 | 25 | function checkout($dir, $gitUrl, ?string $tag) 26 | { 27 | if (!is_dir($dir)) { 28 | run('git clone ' . $gitUrl . ' "' . $dir . '"'); 29 | } 30 | 31 | run('git fetch', $dir); 32 | 33 | if ($tag !== null) { 34 | run('git checkout -f ' . $tag, $dir); 35 | } 36 | } 37 | 38 | function component(Symfony\Component\Console\Output\ConsoleOutput $output, $componentsDir, $name, $gitUrl, ?string $latestVersion) 39 | { 40 | $componentDir = $componentsDir . DIRECTORY_SEPARATOR . $name; 41 | 42 | $output->writeln('Latest version: ' . $latestVersion); 43 | 44 | checkout($componentDir, $gitUrl, $latestVersion); 45 | } 46 | 47 | $input = new Symfony\Component\Console\Input\ArgvInput(); 48 | $output = new Symfony\Component\Console\Output\ConsoleOutput(); 49 | 50 | if ($input->hasParameterOption('--use-asset-dev-server')) { 51 | putenv('USE_ASSET_DEV_SERVER=true'); 52 | } 53 | 54 | if (!$input->hasParameterOption('--no-component-update')) { 55 | foreach ($container['data.components'] as $component) { 56 | $output->writeln(sprintf( 57 | 'Updating component %s...', 58 | $component['repository'] 59 | )); 60 | 61 | component( 62 | $output, 63 | $componentsDir, 64 | $component['name'], 65 | $component['clone_url'], 66 | $component['releases'] ? $component['releases'][0]['tag'] : null 67 | ); 68 | 69 | $output->writeln('Done.'); 70 | } 71 | } 72 | 73 | if ($input->hasParameterOption('--dev-server')) { 74 | $address = $input->getParameterOption( 75 | '--dev-server-address', 76 | '127.0.0.1:8000' 77 | ); 78 | 79 | putenv('DEPLOY_URL=http://' . $address); 80 | 81 | call_user_func( 82 | $container['server'], 83 | getcwd(), 84 | $staticDir, 85 | $output, 86 | $address 87 | ); 88 | 89 | exit; 90 | } 91 | 92 | (new Symfony\Component\Filesystem\Filesystem())->remove($buildDir); 93 | (new Symfony\Component\Filesystem\Filesystem())->mkdir($buildDir); 94 | 95 | (function (Symfony\Component\Console\Output\OutputInterface $output, string $buildDir, array $components): void { 96 | $output->write('Generating changelog.atom...'); 97 | 98 | $releases = array_slice(React\Website\Data\releases($components), 0, 10); 99 | 100 | $feed = new Zend\Feed\Writer\Feed(); 101 | 102 | $feed->setId(getenv('DEPLOY_URL') . '/changelog.html'); 103 | $feed->setLink(getenv('DEPLOY_URL') . '/changelog.html'); 104 | $feed->setFeedLink(getenv('DEPLOY_URL') . '/changelog.atom', 'atom'); 105 | 106 | $feed->setLanguage('en'); 107 | $feed->setTitle('The combined changelog for all ReactPHP components.'); 108 | $feed->setDateModified((int) $releases[0]['date']->format('U')); 109 | 110 | foreach ($releases as $release) { 111 | $entry = $feed->createEntry(); 112 | 113 | $entry->setTitle($release['component'] . ' ' . $release['version']); 114 | $entry->setLink($release['url']); 115 | $entry->setDateModified((int) $release['date']->format('U')); 116 | $entry->setDescription($release['html']); 117 | $entry->addAuthor($release['author']); 118 | 119 | $feed->addEntry($entry); 120 | } 121 | 122 | file_put_contents( 123 | $buildDir . '/changelog.atom', 124 | $feed->export('atom') 125 | ); 126 | 127 | $output->writeln('Done'); 128 | })($output, $buildDir, $container['data.components']); 129 | 130 | call_user_func( 131 | $container['generator'], 132 | getcwd(), 133 | $buildDir, 134 | $output 135 | ); 136 | 137 | if ($input->hasParameterOption('--deploy')) { 138 | (function (Symfony\Component\Console\Output\OutputInterface $output, $buildDir, $deployDir) { 139 | $deployRepository = getenv('DEPLOY_REPO'); 140 | 141 | if (!$deployRepository) { 142 | $output->writeln('No deploy repository configured via the DEPLOY_REPO env variable, skipping deployment.'); 143 | return; 144 | } 145 | 146 | $deployRepositoryUrl = sprintf( 147 | 'git@github.com:%s', 148 | $deployRepository 149 | ); 150 | 151 | $output->writeln('Starting deployment to ' . $deployRepositoryUrl . '.'); 152 | 153 | $filesystem = new Symfony\Component\Filesystem\Filesystem(); 154 | 155 | $filesystem->remove($deployDir); 156 | $filesystem->mkdir($deployDir); 157 | 158 | run('git clone ' . $deployRepositoryUrl . ' "' . $deployDir . '"'); 159 | 160 | $deployBranch = getenv('DEPLOY_TARGET_BRANCH'); 161 | 162 | if (!$deployBranch) { 163 | $deployBranch = 'gh-pages'; 164 | $output->writeln('No deploy branch configured via the DEPLOY_TARGET_BRANCH env variable, using default "gh-pages" branch.'); 165 | } 166 | 167 | try { 168 | run('git checkout ' . $deployBranch, $deployDir); 169 | } catch (Symfony\Component\Process\Exception\ProcessFailedException $e) { 170 | run('git checkout --orphan ' . $deployBranch, $deployDir); 171 | } 172 | 173 | $output->write('Copying generated website...'); 174 | 175 | $finder = new Symfony\Component\Finder\Finder(); 176 | $finder 177 | ->files() 178 | ->in($deployDir) 179 | ->ignoreDotFiles(false) 180 | ->ignoreVCS(true); 181 | 182 | $filesystem->remove($finder); 183 | $filesystem->mirror($buildDir, $deployDir); 184 | 185 | $output->writeln('Done.'); 186 | 187 | try { 188 | run('git add --all . && git commit -m "Update website build"', $deployDir); 189 | } catch (Symfony\Component\Process\Exception\ProcessFailedException $e) { 190 | $output->writeln('No changes in this build, skipping deployment.'); 191 | return; 192 | } 193 | 194 | run('git push origin ' . $deployBranch, $deployDir); 195 | 196 | $output->writeln('Deployment finished.'); 197 | })($output, $buildDir, $deployDir); 198 | } 199 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react/website", 3 | "description": "Source code of reactphp.org", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Jan Sorgalla", 8 | "email": "jsorgalla@gmail.com" 9 | } 10 | ], 11 | "autoload": { 12 | "psr-4": { 13 | "React\\Website\\": "src/" 14 | }, 15 | "files": [ 16 | "src/berti.php", 17 | "src/data.php" 18 | ] 19 | }, 20 | "require-dev": { 21 | "jsor/berti": "^4.1", 22 | "symfony/cache": "^3.4", 23 | "symfony/filesystem": "^3.4", 24 | "symfony/process": "^3.4", 25 | "vlucas/phpdotenv": "^2.6", 26 | "elvanto/litemoji": "^1.4", 27 | "zendframework/zend-feed": "^2.12", 28 | "igorw/retry": "^1.0@dev" 29 | }, 30 | "config": { 31 | "platform": { 32 | "php": "7.1.99" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /config/berti.config.php: -------------------------------------------------------------------------------- 1 | load(); 5 | } catch (Dotenv\Exception\InvalidPathException $e) { 6 | // Ignore missing .env 7 | } 8 | 9 | // use UTC timezone for all dates on website (release dates in changelogs) 10 | date_default_timezone_set('UTC'); 11 | 12 | return function (Pimple\Container $container) { 13 | $container['markdown.cache'] = function () { 14 | return new Symfony\Component\Cache\Adapter\FilesystemAdapter( 15 | 'markdown', 16 | 0, 17 | __DIR__ . '/../tmp/cache' 18 | ); 19 | }; 20 | 21 | $container['data.components'] = function (Pimple\Container $container) { 22 | return React\Website\Data\components( 23 | $container['github.client'], 24 | $container['markdown.cache'] 25 | ); 26 | }; 27 | 28 | $container['data.components_by_category'] = function (Pimple\Container $container) { 29 | return React\Website\Data\components_by_category($container['data.components']); 30 | }; 31 | 32 | $container['data.releases'] = function (Pimple\Container $container) { 33 | return React\Website\Data\releases($container['data.components']); 34 | }; 35 | 36 | $container['data.releases_by_year'] = function (Pimple\Container $container) { 37 | return React\Website\Data\releases_by_year($container['data.releases']); 38 | }; 39 | 40 | $container['data.built_with'] = function (Pimple\Container $container) { 41 | return React\Website\Data\built_with($container['github.client']); 42 | }; 43 | 44 | $container['data.articles'] = function () { 45 | return React\Website\Data\articles(); 46 | }; 47 | 48 | $container['data.talks'] = function () { 49 | return React\Website\Data\talks(); 50 | }; 51 | 52 | $container['template.theme'] = __DIR__ . '/../theme'; 53 | 54 | $container['template.map'] = [ 55 | 'index.html' => 'homepage.html.twig' 56 | ]; 57 | 58 | $container['github.client'] = $container->extend('github.client', function (Github\Client $client) { 59 | $client->addCache( 60 | new Symfony\Component\Cache\Adapter\FilesystemAdapter( 61 | 'github.client', 62 | 0, 63 | __DIR__ . '/../tmp/cache' 64 | ) 65 | ); 66 | 67 | return $client; 68 | }); 69 | 70 | $container['github.url_generator'] = $container->extend('github.url_generator', function (callable $urlGenerator) { 71 | return function (string $repository, string $url, string $cwd = null) use ($urlGenerator) { 72 | return React\Website\Berti\github_url_generator( 73 | $urlGenerator, 74 | $repository, 75 | $url, 76 | $cwd 77 | ); 78 | }; 79 | }); 80 | 81 | $container['github.markdown.filter'] = $container->extend('github.markdown.filter', function (callable $filter, Pimple\Container $container) { 82 | return function ( 83 | string $repository, 84 | string $html, 85 | Berti\Document $document, 86 | array $documentCollection, 87 | array $assetCollection 88 | ) use ($filter, $container) { 89 | return React\Website\Berti\github_markdown_filter( 90 | $container['data.components'], 91 | $container['input.directory_index'], 92 | $container['output.directory_index'], 93 | $filter, 94 | $repository, 95 | $html, 96 | $document, 97 | $documentCollection, 98 | $assetCollection 99 | ); 100 | }; 101 | }); 102 | 103 | $container['markdown.renderer'] = $container->extend('markdown.renderer', function (callable $renderer, Pimple\Container $container) { 104 | return function ( 105 | string $content, 106 | Berti\Document $document, 107 | array $documentCollection, 108 | array $assetCollection 109 | ) use ($renderer, $container) { 110 | /** @var \Psr\Cache\CacheItemPoolInterface $cache */ 111 | $cache = $container['markdown.cache']; 112 | 113 | $repository = $container['github.repository_detector'](dirname($document->input->getRealPath())) ?: null; 114 | 115 | $cacheKey = 'markdown' . md5($repository . $content); 116 | 117 | $cacheItem = $cache->getItem($cacheKey); 118 | 119 | if ($cacheItem->isHit()) { 120 | return $cacheItem->get(); 121 | } 122 | 123 | try { 124 | $html = $renderer( 125 | $content, 126 | $document, 127 | $documentCollection, 128 | $assetCollection 129 | ); 130 | } catch (\Exception $e) { 131 | echo 'Temporary error, will retry in 60s: ' . $e->getMessage() . PHP_EOL; 132 | sleep(60); 133 | $html = $renderer( 134 | $content, 135 | $document, 136 | $documentCollection, 137 | $assetCollection 138 | ); 139 | } 140 | 141 | $cacheItem->set($html); 142 | $cache->save($cacheItem); 143 | 144 | return $html; 145 | }; 146 | }); 147 | 148 | $container['template.renderer'] = $container->extend('template.renderer', function (callable $renderer, Pimple\Container $container) { 149 | return function (string $name, array $context = []) use ($renderer, $container) { 150 | return React\Website\Berti\template_renderer( 151 | $container['data.components'], 152 | $container['github.repository_detector'], 153 | $renderer, 154 | $name, 155 | $context 156 | ); 157 | }; 158 | }); 159 | 160 | $container['twig'] = $container->extend('twig', function (\Twig_Environment $twig, Pimple\Container $container) { 161 | $twig->addExtension(new React\Website\Twig\Extension()); 162 | 163 | $twig->addGlobal( 164 | 'components', 165 | $container['data.components'] 166 | ); 167 | 168 | $twig->addGlobal( 169 | 'components_by_category', 170 | $container['data.components_by_category'] 171 | ); 172 | 173 | $twig->addGlobal( 174 | 'releases', 175 | $container['data.releases'] 176 | ); 177 | 178 | $twig->addGlobal( 179 | 'releases_by_year', 180 | $container['data.releases_by_year'] 181 | ); 182 | 183 | $twig->addGlobal( 184 | 'built_with', 185 | $container['data.built_with'] 186 | ); 187 | 188 | $twig->addGlobal( 189 | 'articles', 190 | $container['data.articles'] 191 | ); 192 | 193 | $twig->addGlobal( 194 | 'talks', 195 | $container['data.talks'] 196 | ); 197 | 198 | $twig->addGlobal( 199 | 'asset_manifest', 200 | json_decode( 201 | file_get_contents(__DIR__ . '/../static-files/assets/manifest.json'), 202 | true 203 | ) 204 | ); 205 | 206 | $twig->addGlobal( 207 | 'use_asset_dev_server', 208 | 'true' === getenv('USE_ASSET_DEV_SERVER') 209 | ); 210 | 211 | $twig->addGlobal( 212 | 'base_url', 213 | rtrim(getenv('DEPLOY_URL'), '/') 214 | ); 215 | 216 | return $twig; 217 | }); 218 | 219 | $container['asset.finder'] = $container->protect(function ($path) { 220 | return (new Symfony\Component\Finder\Finder()) 221 | ->files() 222 | ->ignoreDotFiles(false) 223 | ->notName('.DS_Store') 224 | ->in($path . '/static-files'); 225 | }); 226 | 227 | $container['document.finder'] = $container->protect(function ($path) { 228 | return (new Symfony\Component\Finder\Finder()) 229 | ->name('/\.md$/') 230 | ->name('LICENSE') 231 | ->files() 232 | ->in($path . '/tmp/components') 233 | ->in($path . '/pages'); 234 | }); 235 | }; 236 | -------------------------------------------------------------------------------- /config/webpack-dev-server.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: [ 6 | path.resolve(process.cwd(), 'theme/assets/index.js') 7 | ], 8 | output: { 9 | publicPath: 'http://localhost:8080/_assets/', 10 | filename: '[name].js', 11 | chunkFilename: '[id].js' 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.css', '.json'] 15 | }, 16 | module: { 17 | strictExportPresence: true, 18 | rules: [ 19 | { 20 | test: /\.js$/, 21 | use: [ 22 | { 23 | loader: 'babel-loader', 24 | options: { 25 | cacheDirectory: true, 26 | babelrc: false, 27 | presets: [ 28 | [ 29 | '@babel/preset-env', 30 | { 31 | useBuiltIns: 'entry', 32 | corejs: 3, 33 | modules: false, 34 | debug: false, 35 | } 36 | ] 37 | ], 38 | plugins: [ 39 | '@babel/plugin-syntax-dynamic-import', 40 | ] 41 | }, 42 | }, 43 | ], 44 | }, 45 | { 46 | test: /\.css$/, 47 | use: [ 48 | 'style-loader', 49 | { 50 | loader: 'css-loader', 51 | options: { 52 | importLoaders: 1, 53 | } 54 | }, 55 | { 56 | loader: 'postcss-loader', 57 | options: { 58 | postcssOptions: { 59 | plugins: [ 60 | require('postcss-import')(), 61 | require('postcss-flexbugs-fixes')(), 62 | require('postcss-preset-env')({ 63 | stage: 0, 64 | autoprefixer: { 65 | flexbox: 'no-2009', 66 | grid: true, 67 | } 68 | }), 69 | ] 70 | } 71 | } 72 | } 73 | ], 74 | }, 75 | { 76 | test: /\.(gif|png|jpe?g|svg)$/i, 77 | type: 'asset/resource', 78 | }, 79 | { 80 | test: /\.(woff|woff2|eot|ttf|otf)$/, 81 | type: 'asset/resource', 82 | }, 83 | ], 84 | }, 85 | devtool: 'eval', 86 | devServer: { 87 | static: './static-files/', 88 | headers: { 89 | 'Access-Control-Allow-Origin': '*' 90 | } 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const PostCSSAssetsPlugin = require('postcss-assets-webpack-plugin'); 5 | const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); 6 | 7 | module.exports = () => { 8 | const mode = 'production'; 9 | const targetPath = path.resolve(process.cwd(), 'static-files/assets'); 10 | 11 | return { 12 | mode: mode, 13 | entry: { 14 | 'main': [ 15 | path.resolve(process.cwd(), 'theme/assets/webpack-public-path.js'), 16 | path.resolve(process.cwd(), 'theme/assets/index.js'), 17 | ], 18 | }, 19 | output: { 20 | path: path.resolve(process.cwd(), targetPath), 21 | publicPath: '', // see https://github.com/shellscape/webpack-manifest-plugin/issues/229#issuecomment-737617994 22 | filename: '[name].[contenthash:8].js', 23 | chunkFilename: '[name].[contenthash:8].js', 24 | assetModuleFilename: '[name].[contenthash:8][ext][query]' 25 | }, 26 | optimization: { 27 | runtimeChunk: 'single' 28 | }, 29 | resolve: { 30 | extensions: ['.js', '.css', '.json'] 31 | }, 32 | module: { 33 | strictExportPresence: true, 34 | rules: [ 35 | { 36 | test: /\.js$/, 37 | use: [ 38 | { 39 | loader: 'babel-loader', 40 | options: { 41 | cacheDirectory: true, 42 | babelrc: false, 43 | presets: [ 44 | [ 45 | '@babel/preset-env', 46 | { 47 | useBuiltIns: 'entry', 48 | corejs: 3, 49 | modules: false, 50 | debug: false, 51 | } 52 | ] 53 | ], 54 | plugins: [ 55 | '@babel/plugin-syntax-dynamic-import', 56 | ] 57 | }, 58 | }, 59 | ], 60 | }, 61 | { 62 | test: /\.css$/, 63 | use: [ 64 | mode !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader, 65 | { 66 | loader: 'css-loader', 67 | options: { 68 | importLoaders: 1, 69 | } 70 | }, 71 | { 72 | loader: 'postcss-loader', 73 | options: { 74 | postcssOptions: { 75 | plugins: [ 76 | require('postcss-import')(), 77 | require('postcss-flexbugs-fixes')(), 78 | require('postcss-preset-env')({ 79 | stage: 0, 80 | autoprefixer: { 81 | flexbox: 'no-2009', 82 | grid: true, 83 | } 84 | }), 85 | ] 86 | } 87 | } 88 | } 89 | ], 90 | }, 91 | { 92 | test: /\.(gif|png|jpe?g|svg)$/i, 93 | type: 'asset/resource', 94 | }, 95 | { 96 | test: /\.(woff|woff2|eot|ttf|otf)$/, 97 | type: 'asset/resource', 98 | }, 99 | ], 100 | }, 101 | plugins: [ 102 | new CleanWebpackPlugin(), 103 | new MiniCssExtractPlugin({ 104 | filename: '[name].[contenthash:8].css', 105 | chunkFilename: '[name].[contenthash:8].css', 106 | }), 107 | new PostCSSAssetsPlugin({ 108 | plugins: [ 109 | require('cssnano')({ 110 | preset: ['default', { 111 | discardComments: { 112 | removeAll: true, 113 | }, 114 | }] 115 | }), 116 | ], 117 | }), 118 | new WebpackManifestPlugin({}), 119 | ] 120 | }; 121 | }; 122 | -------------------------------------------------------------------------------- /data/articles.php: -------------------------------------------------------------------------------- 1 | 'Sergey Zhuk', 6 | 'description' => 'A series of articles covering ReactPHP: from the basics to the real application examples.', 7 | 'link' => 'http://sergeyzhuk.me/reactphp-series', 8 | ], 9 | [ 10 | 'title' => 'Cees-Jan Kiewiet', 11 | 'description' => 'Blog series about several ReactPHP components and how they work.', 12 | 'link' => 'http://blog.wyrihaximus.net/categories/reactphp-series/', 13 | ], 14 | [ 15 | 'title' => 'Loïc Faugeron', 16 | 'description' => 'Super Speed Symfony - ReactPHP.', 17 | 'link' => 'https://gnugat.github.io/2016/04/13/super-speed-sf-react-php.html', 18 | ], 19 | [ 20 | 'title' => 'Marc J. Schmidt', 21 | 'description' => 'Bring High Performance Into Your PHP App (with ReactPHP).', 22 | 'link' => 'http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html', 23 | ], 24 | ]; 25 | -------------------------------------------------------------------------------- /data/built_with.php: -------------------------------------------------------------------------------- 1 | 'Thruway', 6 | 'repository' => 'voryx/Thruway' 7 | ], 8 | [ 9 | 'title' => 'PPM - PHP Process Manager', 10 | 'repository' => 'php-pm/php-pm' 11 | ], 12 | [ 13 | 'title' => 'php-ar-drone', 14 | 'repository' => 'jolicode/php-ar-drone' 15 | ], 16 | [ 17 | 'title' => 'Ratchet', 18 | 'repository' => 'ratchetphp/Ratchet' 19 | ], 20 | [ 21 | 'title' => 'Predis\Async', 22 | 'repository' => 'nrk/predis-async' 23 | ], 24 | [ 25 | 'title' => 'clue/redis-server', 26 | 'repository' => 'clue/redis-server' 27 | ], 28 | ]; 29 | -------------------------------------------------------------------------------- /data/components.php: -------------------------------------------------------------------------------- 1 | 'EventLoop', 6 | 'repository' => 'reactphp/event-loop', 7 | 'category' => 'Core Components' 8 | ], 9 | [ 10 | 'title' => 'Stream', 11 | 'repository' => 'reactphp/stream', 12 | 'category' => 'Core Components' 13 | ], 14 | [ 15 | 'title' => 'Promise', 16 | 'repository' => 'reactphp/promise', 17 | 'category' => 'Core Components' 18 | ], 19 | [ 20 | 'title' => 'Async', 21 | 'repository' => 'reactphp/async', 22 | 'category' => 'Core Components' 23 | ], 24 | [ 25 | 'title' => 'Socket', 26 | 'repository' => 'reactphp/socket', 27 | 'category' => 'Network Components' 28 | ], 29 | [ 30 | 'title' => 'Datagram', 31 | 'repository' => 'reactphp/datagram', 32 | 'category' => 'Network Components' 33 | ], 34 | [ 35 | 'title' => 'HTTP', 36 | 'repository' => 'reactphp/http', 37 | 'category' => 'Network Components' 38 | ], 39 | [ 40 | 'title' => 'DNS', 41 | 'repository' => 'reactphp/dns', 42 | 'category' => 'Network Components' 43 | ], 44 | [ 45 | 'title' => 'Cache', 46 | 'repository' => 'reactphp/cache', 47 | 'category' => 'Utility Components' 48 | ], 49 | [ 50 | 'title' => 'ChildProcess', 51 | 'repository' => 'reactphp/child-process', 52 | 'category' => 'Utility Components' 53 | ], 54 | [ 55 | 'title' => 'PromiseTimer', 56 | 'repository' => 'reactphp/promise-timer', 57 | 'category' => 'Utility Components' 58 | ], 59 | [ 60 | 'title' => 'PromiseStream', 61 | 'repository' => 'reactphp/promise-stream', 62 | 'category' => 'Utility Components' 63 | ], 64 | [ 65 | 'title' => 'HttpClient', 66 | 'repository' => 'reactphp/http-client', 67 | 'category' => 'Legacy Components' 68 | ], 69 | [ 70 | 'title' => 'SocketClient', 71 | 'repository' => 'reactphp/socket-client', 72 | 'category' => 'Legacy Components' 73 | ], 74 | ]; 75 | -------------------------------------------------------------------------------- /data/talks.php: -------------------------------------------------------------------------------- 1 | 'https://www.youtube.com/watch?v=-5ZdGUvOqx4', 6 | 'embed_url' => 'https://www.youtube-nocookie.com/embed/-5ZdGUvOqx4' 7 | ], 8 | [ 9 | 'link' => 'https://www.youtube.com/watch?v=s6xrnYae1FU', 10 | 'embed_url' => 'https://www.youtube-nocookie.com/embed/s6xrnYae1FU' 11 | ], 12 | [ 13 | 'link' => 'https://www.youtube.com/watch?v=MWNcItWuKpI', 14 | 'embed_url' => 'https://www.youtube-nocookie.com/embed/MWNcItWuKpI' 15 | ], 16 | ]; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactphp-website", 3 | "version": "2.0.0", 4 | "description": "Source code of reactphp.org", 5 | "repository": "git@github.com:reactphp/website.git", 6 | "author": "Jan Sorgalla ", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "webpack --config ./config/webpack.config.js --progress", 10 | "watch": "webpack --config ./config/webpack.config.js --progress --watch", 11 | "dev-server": "webpack-dev-server --config ./config/webpack-dev-server.config.js --hot" 12 | }, 13 | "browserslist": [ 14 | "> 1%", 15 | "last 4 versions", 16 | "Firefox ESR", 17 | "not ie < 10" 18 | ], 19 | "dependencies": { 20 | "core-js": "^3.21.0", 21 | "ctrly": "^0.7.0", 22 | "docsearch.js": "^2.6.3", 23 | "domestique": "^1.7.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.17.0", 27 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 28 | "@babel/preset-env": "^7.16.11", 29 | "babel-loader": "^8.2.3", 30 | "clean-webpack-plugin": "^4.0.0", 31 | "css-loader": "^6.6.0", 32 | "cssnano": "^5.0.16", 33 | "mini-css-extract-plugin": "^2.5.3", 34 | "postcss-assets-webpack-plugin": "^4.1.2", 35 | "postcss-flexbugs-fixes": "^5.0.2", 36 | "postcss-import": "^14.0.2", 37 | "postcss-loader": "^6.2.1", 38 | "postcss-preset-env": "^7.3.1", 39 | "style-loader": "^3.3.1", 40 | "webpack": "^5.68.0", 41 | "webpack-cli": "^4.9.2", 42 | "webpack-dev-server": "^4.7.4", 43 | "webpack-manifest-plugin": "^4.1.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pages/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The combined changelog for all ReactPHP components. 4 | -------------------------------------------------------------------------------- /pages/index.md: -------------------------------------------------------------------------------- 1 | Placeholder markdown file for the homepage. 2 | -------------------------------------------------------------------------------- /src/Twig/Extension.php: -------------------------------------------------------------------------------- 1 | .*?)(\\1)\)/', $rewriteUrl, $content); 58 | $content = preg_replace_callback('/@import (?!url\()(\'|"|)(?[^\'"\)\n\r]*)\1;?/', $rewriteUrl, $content); 59 | // Handle 'src' values (used in e.g. calls to AlphaImageLoader, which is a proprietary IE filter) 60 | $content = preg_replace_callback('/\bsrc\s*=\s*(["\']?)(?.*?)(\\1)/i', $rewriteUrl, $content); 61 | } 62 | 63 | return $content; 64 | } 65 | 66 | public function participationSvg(string $repo, array $participation): string 67 | { 68 | $width = 320; 69 | $height = 40; 70 | 71 | $prefix = str_replace('/', '-', $repo); 72 | 73 | $x = 0; 74 | $offset = floor($width / count($participation['all'])); 75 | 76 | $points = array_map(function ($value) use (&$x, $offset) { 77 | $currX = $x; 78 | $x += $offset; 79 | 80 | return $currX . ',' . ($value + 1); 81 | }, $participation['all']); 82 | 83 | $pointString = implode(' ', $points); 84 | $rectHeight = $height + 2; 85 | 86 | return << 88 | 89 | 90 | 91 | 92 | 93 | 94 | 101 | 102 | 103 | 104 | 105 | 112 | 113 | 114 | EOF; 115 | } 116 | 117 | public function displayUrl(string $url): string 118 | { 119 | $host = parse_url($url, PHP_URL_HOST); 120 | 121 | if (!$host) { 122 | return $url; 123 | } 124 | 125 | return str_ireplace('www.', '', $host); 126 | } 127 | 128 | public function stripTitle(string $string): string 129 | { 130 | return preg_replace('/^]*?>.*?<\/h1>/si', '', trim($string)); 131 | } 132 | 133 | public function emoji(string $string): string 134 | { 135 | return LitEmoji::encodeUnicode($string); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/berti.php: -------------------------------------------------------------------------------- 1 | run(); 33 | 34 | // Might return HEAD if not in a tag checkout 35 | $version = trim($process->getOutput()); 36 | 37 | if ($version && 'HEAD' !== $version) { 38 | // Always use ../blob/.. because GitHub redirects to ../tree/.. for directories 39 | return 'https://github.com/' . $repository . '/blob/' . $version . '/' . ltrim($url, '/'); 40 | } 41 | 42 | return $bertiUrlGenerator($repository, $url, $cwd); 43 | } 44 | 45 | /** 46 | * This function extends the `Berti\twig_renderer()` to dynamically switch 47 | * templates, eg. if a component repository is detected. 48 | * 49 | * It also passes additional component context data when rendering the template. 50 | */ 51 | function template_renderer( 52 | array $components, 53 | callable $repositoryDetector, 54 | callable $bertiRenderer, 55 | string $name, 56 | array $context = [] 57 | ): string 58 | { 59 | /** @var \Symfony\Component\Finder\SplFileInfo $documentInput */ 60 | $documentInput = $context['berti']['document']->input; 61 | 62 | if ('changelog.md' === $documentInput->getRelativePathname()) { 63 | return $bertiRenderer('changelog.html.twig', $context); 64 | } 65 | 66 | $repo = $repositoryDetector(dirname($documentInput->getRealPath())); 67 | 68 | if (!$repo) { 69 | return $bertiRenderer($name, $context); 70 | } 71 | 72 | $components = array_filter( 73 | $components, 74 | function ($component) use ($repo) { 75 | return $component['full_name'] === $repo; 76 | } 77 | ); 78 | 79 | $component = reset($components); 80 | 81 | if (!$component) { 82 | return $bertiRenderer($name, $context); 83 | } 84 | 85 | $context['component'] = $component; 86 | 87 | $context['component_releases_by_year'] = releases_by_year($component['releases']); 88 | 89 | switch ($documentInput->getFilename()) { 90 | case 'LICENSE': 91 | $name = 'component-license.html.twig'; 92 | break; 93 | case 'CHANGELOG.md': 94 | $name = 'component-changelog.html.twig'; 95 | break; 96 | default: 97 | $name = 'component.html.twig'; 98 | break; 99 | } 100 | 101 | return $bertiRenderer($name, $context); 102 | } 103 | 104 | /** 105 | * Additional markdown filter which replaces links to other components with 106 | * internal relative links. 107 | * 108 | * For example, if there's a markdown link like 109 | * `[Event Loop](https://github.com/reactphp/event-loop)`, the URL is replaced 110 | * with `../event-loop/`. 111 | */ 112 | function github_markdown_filter( 113 | array $components, 114 | string $inputDirectoryIndex, 115 | string $outputDirectoryIndex, 116 | callable $bertiFilter, 117 | string $repository, 118 | string $html, 119 | Document $document, 120 | array $documentCollection, 121 | array $assetCollection 122 | ): string 123 | { 124 | $html = $bertiFilter( 125 | $repository, 126 | $html, 127 | $document, 128 | $documentCollection, 129 | $assetCollection 130 | ); 131 | 132 | $map = []; 133 | 134 | foreach ($documentCollection as $doc) { 135 | $inputPath = $doc->input->getRelativePathname(); 136 | $outputPath = $doc->output->getRelativePathname(); 137 | 138 | if (basename($inputPath) === $inputDirectoryIndex) { 139 | $inputPath = dirname($inputPath); 140 | } 141 | 142 | if (basename($outputPath) === $outputDirectoryIndex) { 143 | $outputPath = rtrim(dirname($outputPath), '/') . '/'; 144 | } 145 | 146 | $map[$inputPath] = uri_rewriter( 147 | $outputPath, 148 | '/', 149 | $document->output->getRelativePathname() 150 | ); 151 | } 152 | 153 | $repos = array_map( 154 | function ($component) { 155 | return $component['name']; 156 | }, 157 | $components 158 | ); 159 | 160 | $callback = function ($matches) use ($map) { 161 | $url = $matches[2] . ($matches[3] ?? ''); 162 | 163 | $hash = ''; 164 | 165 | if (false !== strpos($url, '#')) { 166 | [$url, $hash] = explode('#', $url); 167 | } 168 | 169 | if (!isset($map[$url])) { 170 | return $matches[0]; 171 | } 172 | 173 | if ('' !== $hash) { 174 | $hash = '#' . $hash; 175 | } 176 | 177 | return 'href="' . $map[$url] . $hash . '"'; 178 | }; 179 | 180 | $html = preg_replace_callback( 181 | '/href=(["\']?)https:\/\/github.com\/reactphp\/+([' . preg_quote(implode('|', $repos), '/') . ']+)([^"\']+)?\\1/i', 182 | $callback, 183 | $html 184 | ); 185 | 186 | return $html; 187 | } 188 | -------------------------------------------------------------------------------- /src/data.php: -------------------------------------------------------------------------------- 1 | repo()->contributors($username, $repository); 16 | 17 | $component['participation'] = retry(3, function () use ($client, $username, $repository){ 18 | $participation = $client->repo()->participation($username, $repository); 19 | 20 | if (!isset($participation['all'])) { 21 | throw new \RuntimeException( 22 | "Could not fetch participation for $username/$repository from the Github API" 23 | ); 24 | } 25 | 26 | return $participation; 27 | }); 28 | 29 | $apiReleases = (new ResultPager($client)) 30 | ->fetchAll( 31 | $client->repo()->releases(), 32 | 'all', 33 | [$username, $repository, ['per_page' => 100]] 34 | ); 35 | 36 | $releases = []; 37 | 38 | foreach ($apiReleases as $release) { 39 | $cacheKey = 'gfm' . md5($component['repository'] . $release['body']); 40 | 41 | $cacheItem = $markdownCache->getItem($cacheKey); 42 | 43 | if ($cacheItem->isHit()) { 44 | $html = $cacheItem->get(); 45 | } else { 46 | try{ 47 | $html = $client->markdown()->render( 48 | $release['body'], 49 | 'gfm', 50 | $component['repository'] 51 | ); 52 | } catch (\Exception $e) { 53 | echo 'Temporary error, will retry in 60s: ' . $e->getMessage() . PHP_EOL; 54 | sleep(60); 55 | $html = $client->markdown()->render( 56 | $release['body'], 57 | 'gfm', 58 | $component['repository'] 59 | ); 60 | } 61 | 62 | $cacheItem->set($html); 63 | $markdownCache->save($cacheItem); 64 | } 65 | 66 | $date = new \DateTimeImmutable($release['created_at']); 67 | 68 | $releases[(int) $date->format('U')] = [ 69 | 'version' => ltrim($release['tag_name'], 'v'), 70 | 'tag' => $release['tag_name'], 71 | 'date' => $date, 72 | 'html' => $html, 73 | 'url' => $release['html_url'], 74 | 'component' => $component['title'], 75 | 'repository' => $component['repository'], 76 | 'author' => [ 77 | 'name' => $release['author']['login'], 78 | 'uri' => $release['author']['html_url'], 79 | 'avatar' => $release['author']['avatar_url'], 80 | ] 81 | ]; 82 | } 83 | 84 | krsort($releases, SORT_NATURAL); 85 | 86 | $component['releases'] = array_values($releases); 87 | 88 | return array_merge( 89 | $client->repo()->show($username, $repository), 90 | $component 91 | ); 92 | }, include __DIR__ . '/../data/components.php'); 93 | } 94 | 95 | function components_by_category(array $components): array 96 | { 97 | $byCategory = []; 98 | 99 | foreach ($components as $component) { 100 | if (!isset($byCategory[$component['category']])) { 101 | $byCategory[$component['category']] = []; 102 | } 103 | 104 | $byCategory[$component['category']][] = $component; 105 | } 106 | 107 | return $byCategory; 108 | } 109 | 110 | function releases(array $components): array 111 | { 112 | $releases = []; 113 | 114 | foreach ($components as $component) { 115 | foreach ($component['releases'] as $release) { 116 | $time = (int) $release['date']->format('U'); 117 | 118 | $releases[$time . '-' . $component['repository']] = $release; 119 | } 120 | } 121 | 122 | krsort($releases, SORT_NATURAL); 123 | 124 | return array_values($releases); 125 | } 126 | 127 | function releases_by_year(array $releases): array 128 | { 129 | $byYear = []; 130 | 131 | foreach ($releases as $release) { 132 | $year = (int) $release['date']->format('Y'); 133 | 134 | if (!isset($byYear[$year])) { 135 | $byYear[$year] = []; 136 | } 137 | 138 | $byYear[$year][] = $release; 139 | } 140 | 141 | return $byYear; 142 | } 143 | 144 | function built_with(Client $client): array 145 | { 146 | return array_map(function ($component) use ($client) { 147 | [$username, $repository] = explode('/', $component['repository']); 148 | 149 | return array_merge( 150 | $client->repo()->show($username, $repository), 151 | $component 152 | ); 153 | }, include __DIR__ . '/../data/built_with.php'); 154 | } 155 | 156 | function articles(): array 157 | { 158 | return include __DIR__ . '/../data/articles.php'; 159 | } 160 | 161 | function talks(): array 162 | { 163 | return include __DIR__ . '/../data/talks.php'; 164 | } 165 | -------------------------------------------------------------------------------- /static-files/CNAME: -------------------------------------------------------------------------------- 1 | reactphp.org 2 | -------------------------------------------------------------------------------- /static-files/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/android-chrome-192x192.png -------------------------------------------------------------------------------- /static-files/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/android-chrome-512x512.png -------------------------------------------------------------------------------- /static-files/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/apple-touch-icon.png -------------------------------------------------------------------------------- /static-files/assets/139.3ced3f7b.css: -------------------------------------------------------------------------------- 1 | .algolia-autocomplete{display:block;height:100%;width:100%}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{left:inherit!important;right:0!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{background:transparent;border:none;border-radius:4px;-webkit-box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1);box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1);height:auto;margin:6px 0 0;max-width:600px;min-width:500px;padding:0;position:relative;text-align:left;top:-6px;z-index:999}.algolia-autocomplete .ds-dropdown-menu:before{background:#fff;border-radius:2px;border-right:1px solid #d9d9d9;border-top:1px solid #d9d9d9;content:"";display:block;height:14px;position:absolute;top:-7px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);width:14px;z-index:1000}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{margin-top:8px;position:relative;z-index:1000}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{background:#fff;border:1px solid #d9d9d9;border-radius:4px;overflow:auto;padding:0 8px 8px;position:relative}.algolia-autocomplete .ds-dropdown-menu *{-webkit-box-sizing:border-box;box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{background:#fff;color:#02060c;overflow:hidden;padding:0 8px;position:relative}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{background:rgba(143,187,237,.1);color:#174d8c;padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight{background:inherit;color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{background:inherit;-webkit-box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit;padding:0 0 1px}.algolia-autocomplete .algolia-docsearch-suggestion--content{cursor:pointer;display:block;float:right;padding:5.33333px 0 5.33333px 10.66667px;position:relative;width:70%}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{background:#ddd;content:"";display:block;height:100%;left:-1px;position:absolute;top:0;width:1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{border-bottom:1px solid #ddd;color:#33363d;display:none;font-size:1em;margin-top:8px;padding:4px 0;position:relative}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{float:left;padding:8px 0 0;width:100%}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{word-wrap:break-word;color:#a4a7ae;display:none;float:left;font-size:.9em;padding:5.33333px 10.66667px;position:relative;text-align:right;width:30%}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{background:#ddd;content:"";display:block;height:100%;position:absolute;right:0;top:0;width:1px}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column .algolia-docsearch-suggestion--highlight{background-color:inherit;color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{color:#02060c;font-size:.9em;font-weight:700;margin-bottom:4px}.algolia-autocomplete .algolia-docsearch-suggestion--text{color:#63676d;display:block;font-size:.85em;line-height:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{font-size:1.2em;padding:8px 0;text-align:center;width:100%}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{background-color:#ebebeb;border:none;border-radius:3px;color:#222;font-family:Menlo,Monaco,Consolas,Courier New,monospace;font-size:90%;padding:1px 5px}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary .algolia-docsearch-suggestion--subcategory-column{display:block}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;margin:0;padding:8px}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{padding:0;width:100%}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{border:none;display:block;margin:0;padding:0;width:100%}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{font-size:.85em;opacity:.6}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";display:inline-block;height:10px;width:10px}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{float:left;margin:0;padding:0;width:100%}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-column,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{color:#458ee1;font-size:.9em;font-weight:400;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{color:#458ee1;content:"#";display:inline-block;font-weight:700}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{background:#f8f8f8;display:block;font-size:.85em;line-height:1.4em;margin:4px 0 0;opacity:.8;padding:5.33333px 8px}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{-webkit-box-shadow:none;box-shadow:none;color:#3f4145;font-weight:700}.algolia-autocomplete .algolia-docsearch-footer{float:right;font-size:0;height:20px;line-height:0;margin-top:10.66667px;width:110px;z-index:2000}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;utf8,");background-position:50%;background-repeat:no-repeat;background-size:100%;display:block;height:100%;overflow:hidden;padding:0!important;text-indent:-9000px;width:100%} -------------------------------------------------------------------------------- /static-files/assets/139.820be3d5.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkreactphp_website=self.webpackChunkreactphp_website||[]).push([[139],{7399:function(e,t,i){"use strict";i.r(t),i.d(t,{init:function(){return n}});var a=i(261),c=i.n(a),r=0;function n(e){var t=e.getAttribute("id");t||(t="docsearch-"+r++,e.setAttribute("id",t)),c()({apiKey:"4c440463ddff54a35b4d7dc24afb010b",indexName:"reactphp",inputSelector:"#"+t,debug:"true"===e.getAttribute("data-docsearch-debug"),algoliaOptions:{hitsPerPage:5}})}},5525:function(){}}]); -------------------------------------------------------------------------------- /static-files/assets/261.bd162b4a.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * @overview es6-promise - a tiny implementation of Promises/A+. 3 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 4 | * @license Licensed under MIT license 5 | * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 6 | * @version v4.2.8+1e68dce6 7 | */ 8 | -------------------------------------------------------------------------------- /static-files/assets/553.90d4e0e8.css: -------------------------------------------------------------------------------- 1 | :root{--color-key:#4f5b93;--color-key-light:#a2aacd;--color-base:#584b4f;--color-base-light:#afa1a5;--color-green:#40a977;--color-green-light:#9ad8bb;--color-red:#ba3525;--color-red-light:#e79187;--color-background:#d6d1c8;--color-background-light:#eae8e3;--color-background-lighter:#f4f3f1;--color-background-lightest:#f9f9f8;--box-shadow-light:0 1px 0 0 rgba(0,0,0,.15);--box-shadow-lighter:0 1px 0 0 rgba(0,0,0,.1);--box-shadow-lightest:0 1px 0 0 rgba(0,0,0,.08)}.off-canvas-menu{height:100%;outline:none;overflow-x:hidden;overflow-y:auto;width:300px;z-index:1000}.off-canvas-menu[aria-hidden=false]{pointer-events:auto}.off-canvas-menu__content{-webkit-overflow-scrolling:touch;background:#f9f9f8;background:var(--color-background-lightest);-webkit-box-shadow:0 0 15px rgba(0,0,0,.33);box-shadow:0 0 15px rgba(0,0,0,.33);height:100%;overflow-x:hidden;overflow-y:auto;position:absolute;right:0;-webkit-transition:all .35s cubic-bezier(.23,1,.32,1);-o-transition:all .35s cubic-bezier(.23,1,.32,1);transition:all .35s cubic-bezier(.23,1,.32,1);width:285px}.off-canvas-menu[aria-hidden=false] .off-canvas-menu__content{-webkit-transform:translateZ(0);transform:translateZ(0)}.off-canvas-menu__close{-ms-flex-pack:center;-ms-flex-align:center;align-items:center;-webkit-appearance:none;background:none;border:0;color:#584b4f;color:var(--color-base);cursor:pointer;display:-ms-flexbox;display:flex;font-size:20px;height:48px;justify-content:center;margin:0;outline:none;overflow:visible;padding:0;position:absolute;right:0;text-decoration:none;text-shadow:none;text-transform:none;top:0;width:48px;z-index:1001}.off-canvas-menu__close:hover{color:#40a977;color:var(--color-green)}.off-canvas-menu__section{border-top:1px solid #eae8e3;border-top:1px solid var(--color-background-light);padding:8px 0}.off-canvas-menu__list{list-style:none;margin:0;padding:0}.off-canvas-menu__link{-ms-flex-pack:justify;color:#584b4f;color:var(--color-base);display:-ms-flexbox;display:flex;font-size:.9rem;justify-content:space-between;line-height:1;padding:8px 16px;text-decoration:none}.off-canvas-menu__link:hover{color:#40a977;color:var(--color-green);text-decoration:none}.off-canvas-menu__version{border:1px solid #d6d1c8;border:1px solid var(--color-background);border-radius:4px;color:#afa1a5;color:var(--color-base-light);display:inline-block;font-size:.65rem;line-height:1;padding:.25rem;text-decoration:none}.off-canvas-menu__link:hover .off-canvas-menu__version{border-color:#40a977;border-color:var(--color-green);color:#40a977;color:var(--color-green)}.off-canvas-menu__section-header,.off-canvas-menu__section-top{color:#afa1a5;color:var(--color-base-light);font-size:.9rem!important;font-weight:700;letter-spacing:.1rem;margin:0;padding:8px 16px 16px;text-transform:uppercase}.off-canvas-menu__section-top{color:#584b4f;color:var(--color-base);display:block;font-size:.9rem;line-height:1rem;padding:8px 16px;text-decoration:none}.off-canvas-menu__section-top:hover{color:#40a977;color:var(--color-green);text-decoration:none} -------------------------------------------------------------------------------- /static-files/assets/553.da065a6b.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkreactphp_website=self.webpackChunkreactphp_website||[]).push([[553],{7788:function(t,e,n){n.d(e,{Z:function(){return d}});var r=n(9264),o={selector:"[data-ctrly]",context:null,focusTarget:!0,closeOnBlur:!0,closeOnEsc:!0,closeOnOutsideClick:!0,closeOnScroll:!1,trapFocus:!1,allowMultiple:!1,on:null,autoInit:!0};function i(t){var e={};return[o,t].forEach((function(t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})),e}function a(t){return"which"in t?t.which:t.keyCode}function u(t){return(0,r.sE)('[aria-controls="'.concat(t.id,'"]'))}function c(t){return document.getElementById(t.getAttribute("aria-controls")||t.getAttribute("data-ctrly"))}function s(t){t.removeAttribute("aria-pressed"),t.removeAttribute("aria-controls"),t.removeAttribute("aria-expanded")}var l=0;function d(){var t,e,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=i(n),d=o.selector,f=o.on||{},p={};function b(t){return o.context?(0,r.oq)(t,o.context):document}function v(t,e){return("function"!=typeof f[e]||!1!==f[e](t))&&!1!==(0,r.WI)(t,"ctrly:".concat(e),{bubbles:!0,cancelable:!0})}function h(t){for(var e=t;e;){if(e.id&&p[e.id])return e;e=e.parentElement}}function A(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(!t)return!1;if(!t.hasAttribute("data-ctrly-opened"))return!1;if(!v(t,"close"))return!1;var n=(0,r.AW)(),o=p[t.id]||{},i=o.lastActiveElement,a=o.destroy;return delete p[t.id],a&&a(),u(t).forEach((function(t){"button"!==t.tagName.toLowerCase()&&t.setAttribute("aria-pressed","false"),t.setAttribute("aria-expanded","false")})),t.removeAttribute("data-ctrly-opened"),t.setAttribute("aria-hidden","true"),t.removeAttribute("tabindex"),t.blur(),e&&i&&t.contains(n)&&(0,r.T_)(i,{restoreScrollPosition:!0}),v(t,"closed"),t}function m(t){(0,r.sE)(d,b(t)).forEach((function(e){var n=c(e);n&&n.id!==t.id&&A(n,!1)}))}function y(t,e){var n=[];if(o.closeOnBlur&&!o.trapFocus&&n.push((0,r.on)(document,"focusin",(function(t){e.contains(t.target)||setTimeout((function(){A(e,!1)}),0)}),{capture:!0,passive:!0})),o.closeOnEsc&&n.push((0,r.on)(document,"keydown",(function(t){27===a(t)&&A(e)&&t.preventDefault()}))),o.closeOnOutsideClick&&n.push((0,r.on)(document,"click",(function(t){1!==a(t)||e.contains(t.target)||(0,r.oq)(t.target,d)||A(e)}),{passive:!0})),o.closeOnScroll){var i=!1,u=function(){i=!0},c=function(){i=!1};n.push((0,r.on)(e,"mouseenter",u,{passive:!0})),n.push((0,r.on)(e,"mouseleave",c,{passive:!0})),n.push((0,r.on)(e,"touchstart",u,{passive:!0})),n.push((0,r.on)(e,"touchend",c,{passive:!0})),n.push((0,r.on)(window,"scroll",(function(){i||A(e)}),{passive:!0}))}return o.trapFocus&&n.push((0,r.on)(document,"keydown",(function(t){if(9===a(t)){var n=(0,r.ht)(e);if(!n[0])return t.preventDefault(),void(0,r.T_)(e);var o=(0,r.AW)(),i=n[0],u=n[n.length-1];if(t.shiftKey&&o===i)return t.preventDefault(),void(0,r.T_)(u);t.shiftKey||o!==u||((0,r.T_)(i),t.preventDefault())}}))),function(){for(;n.length;)n.shift().call()}}function g(t){var e=c(t);return e?!e.hasAttribute("data-ctrly-opened")&&!!v(e,"open")&&(p[e.id]={lastActiveElement:(0,r.AW)(),destroy:y(0,e)},u(e).forEach((function(t){"button"!==t.tagName.toLowerCase()&&t.setAttribute("aria-pressed","true"),t.setAttribute("aria-expanded","true")})),e.setAttribute("data-ctrly-opened",""),e.setAttribute("aria-hidden","false"),e.setAttribute("tabindex","-1"),v(e,"opened"),e):(s(t),!1)}function w(t,e){var n=c(e);n?"true"!==e.getAttribute("aria-expanded")?(o.allowMultiple||m(n),g(e),n&&(t.preventDefault(),o.focusTarget&&(0,r.T_)((0,r.ht)(n)[0]||n),n.scrollTop=0,n.scrollLeft=0)):A(n)&&t.preventDefault():A(h(e))&&t.preventDefault()}function E(){t||(t=(0,r.cl)(document,"click",d,(function(t,e){1===a(t)&&w(t,e)})),e=(0,r.cl)(document,"keydown",d,(function(t,e){13!==a(t)&&32!==a(t)||w(t,e)}))),(0,r.sE)(d).forEach((function(t){var e=c(t);if(e){"button"!==t.tagName.toLowerCase()&&(t.hasAttribute("role")||t.setAttribute("role","button"),(0,r.Wq)(t)||t.setAttribute("tabindex","0")),t.setAttribute("aria-controls",e.id);var n=u(e).map((function(t){return t.id||t.setAttribute("id","ctrly-control-"+ ++l),t.id})),o=(e.getAttribute("aria-labelledby")||"").split(" ").concat(n).filter((function(t,e,n){return""!==t&&n.indexOf(t)===e}));e.setAttribute("aria-labelledby",o.join(" ")),"true"===t.getAttribute("aria-expanded")||t.hasAttribute("data-ctrly-open")?g(t):("button"!==t.tagName.toLowerCase()&&t.setAttribute("aria-pressed","false"),t.setAttribute("aria-expanded","false"),e.setAttribute("aria-hidden","true"),e.removeAttribute("tabindex"))}else s(t)}))}function O(n){for(var o in n&&t&&(t(),t=null,e(),e=null),(0,r.sE)(d).forEach((function(t){n&&s(t);var e=c(t);e&&(A(e,!1),n&&e.removeAttribute("aria-hidden"))})),p)Object.prototype.hasOwnProperty.call(p,o)&&(p[o].destroy(),delete p[o])}function x(){O(!1)}function k(){O(!0)}return o.autoInit&&(0,r.Cd)(E),{closeAll:x,destroy:k,init:E}}},553:function(t,e,n){n.r(e),(0,n(7788).Z)({selector:"[data-off-canvas-menu-control]",closeOnScroll:!0})}}]); -------------------------------------------------------------------------------- /static-files/assets/67.4ae9feba.css: -------------------------------------------------------------------------------- 1 | :root{--color-key:#4f5b93;--color-key-light:#a2aacd;--color-base:#584b4f;--color-base-light:#afa1a5;--color-green:#40a977;--color-green-light:#9ad8bb;--color-red:#ba3525;--color-red-light:#e79187;--color-background:#d6d1c8;--color-background-light:#eae8e3;--color-background-lighter:#f4f3f1;--color-background-lightest:#f9f9f8;--box-shadow-light:0 1px 0 0 rgba(0,0,0,.15);--box-shadow-lighter:0 1px 0 0 rgba(0,0,0,.1);--box-shadow-lightest:0 1px 0 0 rgba(0,0,0,.08)}.version-selector__button[aria-expanded=true]{background:#584b4f!important;background:var(--color-base)!important;border-bottom-left-radius:0;border-bottom-right-radius:0;-webkit-box-shadow:none;box-shadow:none;z-index:999}.version-selector__button[aria-expanded=true] [class=icon-chevron-down]{display:none}.version-selector__button[aria-expanded=true] [class=icon-chevron-up]{display:inline-block}.version-selector__panel{background:#584b4f;background:var(--color-base);border-radius:4px;-webkit-box-shadow:0 0 8px rgba(0,0,0,.33);box-shadow:0 0 8px rgba(0,0,0,.33);font-weight:400;left:-115px;outline:none;padding:1rem;width:300px;z-index:998}.version-selector__panel[aria-hidden=false]{position:absolute;top:auto;visibility:visible}.version-selector__list{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;list-style:none;margin-left:-4px;padding:0}.version-selector__list li{margin:4px;width:calc(25% - 8px)}.version-selector__list a{display:block;text-align:center} -------------------------------------------------------------------------------- /static-files/assets/67.ab055693.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkreactphp_website=self.webpackChunkreactphp_website||[]).push([[67],{7788:function(t,e,n){n.d(e,{Z:function(){return d}});var r=n(9264),o={selector:"[data-ctrly]",context:null,focusTarget:!0,closeOnBlur:!0,closeOnEsc:!0,closeOnOutsideClick:!0,closeOnScroll:!1,trapFocus:!1,allowMultiple:!1,on:null,autoInit:!0};function i(t){var e={};return[o,t].forEach((function(t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})),e}function a(t){return"which"in t?t.which:t.keyCode}function u(t){return(0,r.sE)('[aria-controls="'.concat(t.id,'"]'))}function c(t){return document.getElementById(t.getAttribute("aria-controls")||t.getAttribute("data-ctrly"))}function s(t){t.removeAttribute("aria-pressed"),t.removeAttribute("aria-controls"),t.removeAttribute("aria-expanded")}var l=0;function d(){var t,e,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=i(n),d=o.selector,f=o.on||{},p={};function b(t){return o.context?(0,r.oq)(t,o.context):document}function v(t,e){return("function"!=typeof f[e]||!1!==f[e](t))&&!1!==(0,r.WI)(t,"ctrly:".concat(e),{bubbles:!0,cancelable:!0})}function h(t){for(var e=t;e;){if(e.id&&p[e.id])return e;e=e.parentElement}}function A(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(!t)return!1;if(!t.hasAttribute("data-ctrly-opened"))return!1;if(!v(t,"close"))return!1;var n=(0,r.AW)(),o=p[t.id]||{},i=o.lastActiveElement,a=o.destroy;return delete p[t.id],a&&a(),u(t).forEach((function(t){"button"!==t.tagName.toLowerCase()&&t.setAttribute("aria-pressed","false"),t.setAttribute("aria-expanded","false")})),t.removeAttribute("data-ctrly-opened"),t.setAttribute("aria-hidden","true"),t.removeAttribute("tabindex"),t.blur(),e&&i&&t.contains(n)&&(0,r.T_)(i,{restoreScrollPosition:!0}),v(t,"closed"),t}function m(t){(0,r.sE)(d,b(t)).forEach((function(e){var n=c(e);n&&n.id!==t.id&&A(n,!1)}))}function y(t,e){var n=[];if(o.closeOnBlur&&!o.trapFocus&&n.push((0,r.on)(document,"focusin",(function(t){e.contains(t.target)||setTimeout((function(){A(e,!1)}),0)}),{capture:!0,passive:!0})),o.closeOnEsc&&n.push((0,r.on)(document,"keydown",(function(t){27===a(t)&&A(e)&&t.preventDefault()}))),o.closeOnOutsideClick&&n.push((0,r.on)(document,"click",(function(t){1!==a(t)||e.contains(t.target)||(0,r.oq)(t.target,d)||A(e)}),{passive:!0})),o.closeOnScroll){var i=!1,u=function(){i=!0},c=function(){i=!1};n.push((0,r.on)(e,"mouseenter",u,{passive:!0})),n.push((0,r.on)(e,"mouseleave",c,{passive:!0})),n.push((0,r.on)(e,"touchstart",u,{passive:!0})),n.push((0,r.on)(e,"touchend",c,{passive:!0})),n.push((0,r.on)(window,"scroll",(function(){i||A(e)}),{passive:!0}))}return o.trapFocus&&n.push((0,r.on)(document,"keydown",(function(t){if(9===a(t)){var n=(0,r.ht)(e);if(!n[0])return t.preventDefault(),void(0,r.T_)(e);var o=(0,r.AW)(),i=n[0],u=n[n.length-1];if(t.shiftKey&&o===i)return t.preventDefault(),void(0,r.T_)(u);t.shiftKey||o!==u||((0,r.T_)(i),t.preventDefault())}}))),function(){for(;n.length;)n.shift().call()}}function g(t){var e=c(t);return e?!e.hasAttribute("data-ctrly-opened")&&!!v(e,"open")&&(p[e.id]={lastActiveElement:(0,r.AW)(),destroy:y(0,e)},u(e).forEach((function(t){"button"!==t.tagName.toLowerCase()&&t.setAttribute("aria-pressed","true"),t.setAttribute("aria-expanded","true")})),e.setAttribute("data-ctrly-opened",""),e.setAttribute("aria-hidden","false"),e.setAttribute("tabindex","-1"),v(e,"opened"),e):(s(t),!1)}function w(t,e){var n=c(e);n?"true"!==e.getAttribute("aria-expanded")?(o.allowMultiple||m(n),g(e),n&&(t.preventDefault(),o.focusTarget&&(0,r.T_)((0,r.ht)(n)[0]||n),n.scrollTop=0,n.scrollLeft=0)):A(n)&&t.preventDefault():A(h(e))&&t.preventDefault()}function E(){t||(t=(0,r.cl)(document,"click",d,(function(t,e){1===a(t)&&w(t,e)})),e=(0,r.cl)(document,"keydown",d,(function(t,e){13!==a(t)&&32!==a(t)||w(t,e)}))),(0,r.sE)(d).forEach((function(t){var e=c(t);if(e){"button"!==t.tagName.toLowerCase()&&(t.hasAttribute("role")||t.setAttribute("role","button"),(0,r.Wq)(t)||t.setAttribute("tabindex","0")),t.setAttribute("aria-controls",e.id);var n=u(e).map((function(t){return t.id||t.setAttribute("id","ctrly-control-"+ ++l),t.id})),o=(e.getAttribute("aria-labelledby")||"").split(" ").concat(n).filter((function(t,e,n){return""!==t&&n.indexOf(t)===e}));e.setAttribute("aria-labelledby",o.join(" ")),"true"===t.getAttribute("aria-expanded")||t.hasAttribute("data-ctrly-open")?g(t):("button"!==t.tagName.toLowerCase()&&t.setAttribute("aria-pressed","false"),t.setAttribute("aria-expanded","false"),e.setAttribute("aria-hidden","true"),e.removeAttribute("tabindex"))}else s(t)}))}function O(n){for(var o in n&&t&&(t(),t=null,e(),e=null),(0,r.sE)(d).forEach((function(t){n&&s(t);var e=c(t);e&&(A(e,!1),n&&e.removeAttribute("aria-hidden"))})),p)Object.prototype.hasOwnProperty.call(p,o)&&(p[o].destroy(),delete p[o])}function x(){O(!1)}function k(){O(!0)}return o.autoInit&&(0,r.Cd)(E),{closeAll:x,destroy:k,init:E}}},4067:function(t,e,n){n.r(e),(0,n(7788).Z)({selector:"[data-version-selector-control]"})}}]); -------------------------------------------------------------------------------- /static-files/assets/icons.b4abbede.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /static-files/assets/icons.cf61dfff.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/assets/icons.cf61dfff.woff -------------------------------------------------------------------------------- /static-files/assets/icons.f41c5641.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/assets/icons.f41c5641.ttf -------------------------------------------------------------------------------- /static-files/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "main.css": "main.09dbcb8e.css", 3 | "main.js": "main.93d54642.js", 4 | "runtime.js": "runtime.124568e7.js", 5 | "553.90d4e0e8.css": "553.90d4e0e8.css", 6 | "553.da065a6b.js": "553.da065a6b.js", 7 | "139.3ced3f7b.css": "139.3ced3f7b.css", 8 | "139.820be3d5.js": "139.820be3d5.js", 9 | "67.4ae9feba.css": "67.4ae9feba.css", 10 | "67.ab055693.js": "67.ab055693.js", 11 | "261.bd162b4a.js": "261.bd162b4a.js", 12 | "sourcesanspro-regular.woff": "sourcesanspro-regular.f84b2bd4.woff", 13 | "sourcesanspro-bold.woff": "sourcesanspro-bold.cb7d3610.woff", 14 | "icons.svg?ae19fi": "icons.b4abbede.svg?ae19fi", 15 | "icons.woff?ae19fi": "icons.cf61dfff.woff?ae19fi", 16 | "icons.ttf?ae19fi": "icons.f41c5641.ttf?ae19fi", 17 | "search.svg": "search.17d09555.svg" 18 | } -------------------------------------------------------------------------------- /static-files/assets/runtime.124568e7.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var e,t,n,r,o,i={},u={};function a(e){var t=u[e];if(void 0!==t)return t.exports;var n=u[e]={exports:{}};return i[e].call(n.exports,n,n.exports,a),n.exports}a.m=i,e=[],a.O=function(t,n,r,o){if(!n){var i=1/0;for(l=0;l=o)&&Object.keys(a.O).every((function(e){return a.O[e](n[f])}))?n.splice(f--,1):(u=!1,o0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[n,r,o]},a.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(t,{a:t}),t},a.d=function(e,t){for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.f={},a.e=function(e){return Promise.all(Object.keys(a.f).reduce((function(t,n){return a.f[n](e,t),t}),[]))},a.u=function(e){return e+"."+{67:"ab055693",139:"820be3d5",261:"bd162b4a",553:"da065a6b"}[e]+".js"},a.miniCssF=function(e){return e+"."+{67:"4ae9feba",139:"3ced3f7b",553:"90d4e0e8"}[e]+".css"},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t={},n="reactphp-website:",a.l=function(e,r,o,i){if(t[e])t[e].push(r);else{var u,f;if(void 0!==o)for(var c=document.getElementsByTagName("script"),l=0;lsearch 2 | -------------------------------------------------------------------------------- /static-files/assets/sourcesanspro-bold.cb7d3610.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/assets/sourcesanspro-bold.cb7d3610.woff -------------------------------------------------------------------------------- /static-files/assets/sourcesanspro-regular.f84b2bd4.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/assets/sourcesanspro-regular.f84b2bd4.woff -------------------------------------------------------------------------------- /static-files/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #f4f3f1 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /static-files/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/favicon-32x32.png -------------------------------------------------------------------------------- /static-files/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/favicon.ico -------------------------------------------------------------------------------- /static-files/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactPHP", 3 | "icons": [ 4 | { 5 | "src": "android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#f4f3f1", 16 | "background_color": "#f4f3f1", 17 | "display": "standalone" 18 | } -------------------------------------------------------------------------------- /static-files/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/mstile-150x150.png -------------------------------------------------------------------------------- /static-files/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactphp/website/c2482860b9334add7909db0840a2d793afca45e3/static-files/og-image.png -------------------------------------------------------------------------------- /static-files/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /static-files/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/assets/components/component-info/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /theme/assets/components/component-info/style.css: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables.css"; 2 | 3 | .component-info { 4 | position: sticky; 5 | top: 16px; 6 | bottom: 16px; 7 | padding: 24px; 8 | background-color: var(--color-background-lighter); 9 | box-shadow: var(--box-shadow-lighter); 10 | border-radius: 4px; 11 | color: var(--color-base); 12 | 13 | font-size: .875rem; 14 | 15 | & a, 16 | & a [class*="icon"] { 17 | color: var(--color-base); 18 | } 19 | 20 | & a:hover { 21 | color: var(--color-base-light); 22 | text-decoration: none; 23 | } 24 | 25 | & pre { 26 | line-height: 1; 27 | font-size: .75rem; 28 | } 29 | } 30 | 31 | .component-info__title { 32 | display: flex; 33 | font-size: 1.5rem; 34 | line-height: 1rem; 35 | margin-bottom: 1rem; 36 | color: var(--color-base); 37 | align-items: center; 38 | 39 | & a { 40 | text-decoration: none; 41 | } 42 | 43 | & > *:not(:last-child) { 44 | margin-right: 8px; 45 | } 46 | } 47 | 48 | .component-info__subtitle { 49 | font-size: .875rem; 50 | line-height: .875rem; 51 | font-weight: bold; 52 | text-transform: uppercase; 53 | letter-spacing: .1em; 54 | margin-bottom: .875rem; 55 | color: var(--color-base-light); 56 | } 57 | 58 | .component-info__info { 59 | padding: 0; 60 | list-style: none; 61 | display: flex; 62 | flex-flow: row; 63 | justify-content: flex-start; 64 | 65 | & li { 66 | margin-right: 16px; 67 | 68 | &:last-child { 69 | margin-right: 0; 70 | } 71 | } 72 | 73 | & a { 74 | font-size: .75rem; 75 | text-decoration: none; 76 | white-space: nowrap; 77 | text-overflow: ellipsis; 78 | } 79 | } 80 | 81 | .component-info__contributors { 82 | display: flex; 83 | flex-wrap: wrap; 84 | padding: 0; 85 | list-style: none; 86 | 87 | & li { 88 | padding: 0 4px 4px 0; 89 | margin: 0; 90 | } 91 | 92 | & img { 93 | display: block; 94 | width: 40px; 95 | height: auto; 96 | border-radius: 2px; 97 | box-shadow: var(--box-shadow-lighter); 98 | } 99 | } 100 | 101 | .component-info__participation { 102 | background-color: var(--color-background-lightest); 103 | box-shadow: var(--box-shadow-lighter); 104 | padding: 8px; 105 | border-radius: 4px; 106 | text-align: center; 107 | 108 | & svg { 109 | display: block; 110 | margin: auto; 111 | max-width: 100%; 112 | height: auto !important; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /theme/assets/components/docsearch/algolia-autocomplete.css: -------------------------------------------------------------------------------- 1 | .algolia-autocomplete { 2 | display: block; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu { 8 | right: 0 !important; 9 | left: inherit !important; 10 | } 11 | 12 | .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before { 13 | right: 48px; 14 | } 15 | 16 | .algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu { 17 | left: 0 !important; 18 | right: inherit !important; 19 | } 20 | 21 | .algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before { 22 | left: 48px; 23 | } 24 | 25 | .algolia-autocomplete .ds-dropdown-menu { 26 | position: relative; 27 | top: -6px; 28 | border-radius: 4px; 29 | margin: 6px 0 0; 30 | padding: 0; 31 | text-align: left; 32 | height: auto; 33 | position: relative; 34 | background: transparent; 35 | border: none; 36 | z-index: 999; 37 | max-width: 600px; 38 | min-width: 500px; 39 | box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1); 40 | } 41 | 42 | .algolia-autocomplete .ds-dropdown-menu:before { 43 | display: block; 44 | position: absolute; 45 | content: ''; 46 | width: 14px; 47 | height: 14px; 48 | background: #fff; 49 | z-index: 1000; 50 | top: -7px; 51 | border-top: 1px solid #d9d9d9; 52 | border-right: 1px solid #d9d9d9; 53 | -webkit-transform: rotate(-45deg); 54 | transform: rotate(-45deg); 55 | border-radius: 2px; 56 | } 57 | 58 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestions { 59 | position: relative; 60 | z-index: 1000; 61 | margin-top: 8px; 62 | } 63 | 64 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestion { 65 | cursor: pointer; 66 | } 67 | 68 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple { 69 | background-color: rgba(69, 142, 225, 0.05); 70 | } 71 | 72 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content { 73 | background-color: rgba(69, 142, 225, 0.05); 74 | } 75 | 76 | .algolia-autocomplete .ds-dropdown-menu [class^="ds-dataset-"] { 77 | position: relative; 78 | border: solid 1px #d9d9d9; 79 | background: #fff; 80 | border-radius: 4px; 81 | overflow: auto; 82 | padding: 0 8px 8px; 83 | } 84 | 85 | .algolia-autocomplete .ds-dropdown-menu * { 86 | box-sizing: border-box; 87 | } 88 | 89 | .algolia-autocomplete .algolia-docsearch-suggestion { 90 | position: relative; 91 | padding: 0 8px; 92 | background: #fff; 93 | color: #02060C; 94 | overflow: hidden; 95 | } 96 | 97 | .algolia-autocomplete .algolia-docsearch-suggestion--highlight { 98 | color: #174d8c; 99 | background: rgba(143, 187, 237, 0.1); 100 | padding: 0.1em 0.05em; 101 | } 102 | 103 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight, 104 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight { 105 | color: inherit; 106 | background: inherit; 107 | } 108 | 109 | .algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { 110 | padding: 0 0 1px; 111 | background: inherit; 112 | box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8); 113 | color: inherit; 114 | } 115 | 116 | .algolia-autocomplete .algolia-docsearch-suggestion--content { 117 | display: block; 118 | float: right; 119 | width: 70%; 120 | position: relative; 121 | padding: 5.33333px 0 5.33333px 10.66667px; 122 | cursor: pointer; 123 | } 124 | 125 | .algolia-autocomplete .algolia-docsearch-suggestion--content:before { 126 | content: ''; 127 | position: absolute; 128 | display: block; 129 | top: 0; 130 | height: 100%; 131 | width: 1px; 132 | background: #ddd; 133 | left: -1px; 134 | } 135 | 136 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header { 137 | position: relative; 138 | border-bottom: 1px solid #ddd; 139 | display: none; 140 | margin-top: 8px; 141 | padding: 4px 0; 142 | font-size: 1em; 143 | color: #33363D; 144 | } 145 | 146 | .algolia-autocomplete .algolia-docsearch-suggestion--wrapper { 147 | width: 100%; 148 | float: left; 149 | padding: 8px 0 0 0; 150 | } 151 | 152 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { 153 | float: left; 154 | width: 30%; 155 | display: none; 156 | padding-left: 0; 157 | text-align: right; 158 | position: relative; 159 | padding: 5.33333px 10.66667px; 160 | color: #A4A7AE; 161 | font-size: 0.9em; 162 | word-wrap: break-word; 163 | } 164 | 165 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before { 166 | content: ''; 167 | position: absolute; 168 | display: block; 169 | top: 0; 170 | height: 100%; 171 | width: 1px; 172 | background: #ddd; 173 | right: 0; 174 | } 175 | 176 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column .algolia-docsearch-suggestion--highlight { 177 | background-color: inherit; 178 | color: inherit; 179 | } 180 | 181 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline { 182 | display: none; 183 | } 184 | 185 | .algolia-autocomplete .algolia-docsearch-suggestion--title { 186 | margin-bottom: 4px; 187 | color: #02060C; 188 | font-size: 0.9em; 189 | font-weight: bold; 190 | } 191 | 192 | .algolia-autocomplete .algolia-docsearch-suggestion--text { 193 | display: block; 194 | line-height: 1.2em; 195 | font-size: 0.85em; 196 | color: #63676D; 197 | } 198 | 199 | .algolia-autocomplete .algolia-docsearch-suggestion--no-results { 200 | width: 100%; 201 | padding: 8px 0; 202 | text-align: center; 203 | font-size: 1.2em; 204 | } 205 | 206 | .algolia-autocomplete .algolia-docsearch-suggestion--no-results::before { 207 | display: none; 208 | } 209 | 210 | .algolia-autocomplete .algolia-docsearch-suggestion code { 211 | padding: 1px 5px; 212 | font-size: 90%; 213 | border: none; 214 | color: #222222; 215 | background-color: #EBEBEB; 216 | border-radius: 3px; 217 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 218 | } 219 | 220 | .algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight { 221 | background: none; 222 | } 223 | 224 | .algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header { 225 | display: block; 226 | } 227 | 228 | .algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary .algolia-docsearch-suggestion--subcategory-column { 229 | display: block; 230 | } 231 | 232 | .algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion { 233 | border-bottom: solid 1px #eee; 234 | padding: 8px; 235 | margin: 0; 236 | } 237 | 238 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content { 239 | width: 100%; 240 | padding: 0; 241 | } 242 | 243 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content::before { 244 | display: none; 245 | } 246 | 247 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header { 248 | margin: 0; 249 | padding: 0; 250 | display: block; 251 | width: 100%; 252 | border: none; 253 | } 254 | 255 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0 { 256 | opacity: .6; 257 | font-size: 0.85em; 258 | } 259 | 260 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1 { 261 | opacity: .6; 262 | font-size: 0.85em; 263 | } 264 | 265 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1::before { 266 | background-image: url('data:image/svg+xml;utf8,'); 267 | content: ''; 268 | width: 10px; 269 | height: 10px; 270 | display: inline-block; 271 | } 272 | 273 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper { 274 | width: 100%; 275 | float: left; 276 | margin: 0; 277 | padding: 0; 278 | } 279 | 280 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-column, .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content, .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline { 281 | display: none !important; 282 | } 283 | 284 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title { 285 | margin: 0; 286 | color: #458EE1; 287 | font-size: 0.9em; 288 | font-weight: normal; 289 | } 290 | 291 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title::before { 292 | content: "#"; 293 | font-weight: bold; 294 | color: #458EE1; 295 | display: inline-block; 296 | } 297 | 298 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text { 299 | margin: 4px 0 0; 300 | display: block; 301 | line-height: 1.4em; 302 | padding: 5.33333px 8px; 303 | background: #f8f8f8; 304 | font-size: 0.85em; 305 | opacity: .8; 306 | } 307 | 308 | .algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { 309 | color: #3f4145; 310 | font-weight: bold; 311 | box-shadow: none; 312 | } 313 | 314 | .algolia-autocomplete .algolia-docsearch-footer { 315 | width: 110px; 316 | height: 20px; 317 | z-index: 2000; 318 | margin-top: 10.66667px; 319 | float: right; 320 | font-size: 0; 321 | line-height: 0; 322 | } 323 | 324 | .algolia-autocomplete .algolia-docsearch-footer--logo { 325 | background-image: url("data:image/svg+xml;utf8,"); 326 | background-repeat: no-repeat; 327 | background-position: center; 328 | background-size: 100%; 329 | overflow: hidden; 330 | text-indent: -9000px; 331 | padding: 0 !important; 332 | width: 100%; 333 | height: 100%; 334 | display: block; 335 | } 336 | -------------------------------------------------------------------------------- /theme/assets/components/docsearch/algolia-autocomplete.js: -------------------------------------------------------------------------------- 1 | import docsearch from 'docsearch.js'; 2 | 3 | import './algolia-autocomplete.css'; 4 | 5 | let idCounter = 0; 6 | 7 | export function init(input) { 8 | let id = input.getAttribute('id'); 9 | 10 | if (!id) { 11 | id = 'docsearch-' + idCounter++; 12 | input.setAttribute('id', id); 13 | } 14 | 15 | docsearch({ 16 | apiKey: '4c440463ddff54a35b4d7dc24afb010b', 17 | indexName: 'reactphp', 18 | inputSelector: '#' + id, 19 | debug: 'true' === input.getAttribute('data-docsearch-debug'), 20 | algoliaOptions: { 21 | hitsPerPage: 5 22 | } 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /theme/assets/components/docsearch/index.js: -------------------------------------------------------------------------------- 1 | import {ready, find} from 'domestique'; 2 | 3 | // Workaround for https://github.com/algolia/algoliasearch-client-javascript/issues/691 4 | window.process = { 5 | env: { DEBUG: undefined } 6 | }; 7 | 8 | ready(() => { 9 | find('[data-docsearch]:not([data-docsearch-initialized])').forEach(element => { 10 | element.setAttribute('data-docsearch-initialized', true); 11 | 12 | import('./algolia-autocomplete.js').then(module => { 13 | module.init(element); 14 | }); 15 | }) 16 | }); 17 | -------------------------------------------------------------------------------- /theme/assets/components/off-canvas-menu/index.css: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables.css"; 2 | 3 | .off-canvas-menu { 4 | position: fixed; 5 | top: 0; 6 | right: 0; 7 | pointer-events: none; 8 | } 9 | 10 | .off-canvas-menu__content { 11 | transform: translate3d(105%, 0, 0); 12 | } 13 | -------------------------------------------------------------------------------- /theme/assets/components/off-canvas-menu/index.js: -------------------------------------------------------------------------------- 1 | import './index.css'; 2 | 3 | import('./menu.js'); 4 | -------------------------------------------------------------------------------- /theme/assets/components/off-canvas-menu/menu.css: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables.css"; 2 | 3 | .off-canvas-menu { 4 | width: 300px; 5 | height: 100%; 6 | z-index: 1000; 7 | overflow-y: auto; 8 | overflow-x: hidden; 9 | outline: none; 10 | 11 | &[aria-hidden="false"] { 12 | pointer-events: auto; 13 | } 14 | } 15 | 16 | .off-canvas-menu__content { 17 | position: absolute; 18 | right: 0; 19 | width: 285px; 20 | height: 100%; 21 | overflow-y: auto; 22 | overflow-x: hidden; 23 | background: var(--color-background-lightest); 24 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.33); 25 | 26 | -webkit-overflow-scrolling: touch; 27 | transition: all .35s cubic-bezier(0.23, 1, 0.32, 1); 28 | } 29 | 30 | .off-canvas-menu[aria-hidden="false"] { 31 | & .off-canvas-menu__content { 32 | transform: translate3D(0, 0, 0); 33 | } 34 | } 35 | 36 | .off-canvas-menu__close { 37 | /* 33 | 34 | 54 | 55 | 56 | 57 | 74 | 75 |

Installation

76 | 77 |
composer require {{ component.repository|replace({'php/': '/'}) }}:{{ '^' ~ component.releases[0].version }}
78 | 79 |

Contributors

80 | 81 |
    82 | {% for contributor in component.contributors %} 83 |
  • 84 | 85 | {{ contributor.login }} 86 | 87 |
  • 88 | {% endfor %} 89 |
90 | 91 |

Activity

92 | 93 |
94 | {{ participation_svg(component.repository, component.participation)|raw }} 95 |
96 | 97 | 98 | 99 | 100 | 101 | {% endblock %} 102 | -------------------------------------------------------------------------------- /theme/default.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% spaceless %}{% block title %}{{ berti.title }}{% endblock %} - ReactPHP{% endspaceless %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% if not use_asset_dev_server %} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% else %} 32 | 33 | {% endif %} 34 | {% block head %}{% endblock %} 35 | 36 | 37 | 38 | {% block header %} 39 |
40 |
41 |
42 | 45 | 48 | {% include 'partials/header_menu.html.twig' %} 49 |
50 |
51 |
52 | {% endblock %} 53 | 54 | {% block content %} 55 |
56 |
57 | {{ berti.content|emoji|raw }} 58 |
59 |
60 | {% endblock %} 61 | 62 | {% block footer %} 63 | 72 | {% endblock %} 73 | 74 | 75 | -------------------------------------------------------------------------------- /theme/partials/changelog.html.twig: -------------------------------------------------------------------------------- 1 |
    2 | {% for year, releases in releases_by_year %} 3 |
  • {{ year }}
  • 4 | {% endfor %} 5 |
6 | 7 | {% for year, releases in releases_by_year %} 8 |

9 | 10 | {{ year }} 11 |

12 | 13 | {% for release in releases %} 14 | 15 | {% set _id = release.version|replace({'.': ''}) ~ '-' ~ release.date|date('Y-m-d') %} 16 | {% if is_combined_changelog == true %} 17 | {% set _id = release.component|lower ~ '-' ~ _id %} 18 | {% endif %} 19 |

20 | 21 | 22 | {% if is_combined_changelog == true %}{{ release.component }} {% endif %}{{ release.version }} 23 | 24 | 25 | ({{ release.date|date('Y-m-d') }}) 26 | 27 | Release on GitHub 28 | 29 | 30 |

31 | 32 | {{ release.html|raw }} 33 | 34 |
35 | {% endfor %} 36 | {% endfor %} 37 | -------------------------------------------------------------------------------- /theme/partials/header_menu.html.twig: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 | 43 | --------------------------------------------------------------------------------