diff --git a/components/ParshaPicker.js b/components/ParshaPicker.js index f61e899c..49516b18 100644 --- a/components/ParshaPicker.js +++ b/components/ParshaPicker.js @@ -1,4 +1,5 @@ import parshiyot from '../build/parshiyot.json' +import fuzzy from '../src/fuzzy' const Parsha = ({ ref, he }) => `
  • `
  • ` +/* +
  • +

    שלח

    +

    Shelach

    +
  • +
  • +

    בשלח

    +

    Beshalach

    +
  • +
  • +

    וישלח

    +

    Vayishlach

    +
  • +*/ + +/* +
  • +

    No results

    +
  • +*/ +const Search = () => ` + +` + +const decorateString = ({ string, atIndexes, withDecoration }) => { + let indexesIndex = 0 + return string + .split('') + .map((char, i) => { + if (atIndexes[indexesIndex] !== i) return char + + ++indexesIndex + return withDecoration(char) + }, '') + .join('') +} + +const SearchResult = ({ string, indexes }) => ` +
  • +

    ${decorateString({ + string, + atIndexes: indexes, + withDecoration: (c) => (`${c}`) + })} +

    +
  • +` + const ParshaPicker = () => `
    - + ${Search()}
      ${parshiyot .reduce((books, parsha) => { @@ -40,4 +93,39 @@ const ParshaPicker = () => `
    ` +const searchResults = (query) => fuzzy(parshiyot.map(parsha => parsha.en), query) + +const htmlToElement = (html) => { + const template = document.createElement('template') + html = html.trim() // Never return a text node of whitespace as the result + template.innerHTML = html + return template.content.firstChild +} + +const top = (n) => (_, i) => i < n + +const search = ({ jumper, query }) => { + const searchResultsElement = jumper.querySelector('.search-results') + + if (query) { + searchResultsElement.classList.remove('u-hidden') + jumper.querySelector('.parsha-books').classList.add('u-hidden') + + searchResultsElement.innerHTML = '' + + searchResults(query) + .filter(top(5)) + .map(SearchResult) + .map(htmlToElement) + .forEach(result => { + searchResultsElement.appendChild(result) + }) + } else { + searchResultsElement.classList.add('u-hidden') + jumper.querySelector('.parsha-books').classList.remove('u-hidden') + } +} + +export { search } + export default ParshaPicker diff --git a/css/master.css b/css/master.css index 46a544db..8d29c137 100644 --- a/css/master.css +++ b/css/master.css @@ -15,10 +15,13 @@ * { box-sizing: border-box; + margin: 0; + padding: 0; } body { height: 100vh; + font-family: sans-serif; } ol { diff --git a/css/parsha-picker.css b/css/parsha-picker.css index bc02bebe..af33e42d 100644 --- a/css/parsha-picker.css +++ b/css/parsha-picker.css @@ -26,31 +26,65 @@ padding: 2em 1em; } -.parsha-search { - position: relative; - font-family: sans-serif; - border-bottom: 2px solid hsla(0, 0%, 0%, 0.25); +.search { + direction: ltr; } -.parsha-search-input { - border: none; - direction: ltr; - padding: 0.1em 2em 0.1em 0.5em; - width: 30ch; +.search { + background-color: hsla(0, 0%, 0%, 0.05); + display: inline-block; + border-radius: 4px; color: hsla(0, 0%, 0%, 0.7); + border: 1px solid hsla(0, 0%, 0%, 0); + width: 30ch; } -.parsha-search-input:focus { - outline: none; - border-color: hsla(0, 0%, 0%, 0.5); +.search:focus-within { + border: 1px solid hsla(0, 0%, 0%, 0.1); } -.parsha-search-icon { - position: absolute; - right: 10px; +.search-bar { + display: flex; + padding: 0.5em 1em; +} + +.search-bar > *:not(:first-child) { + margin-left: 1em; +} + +.search-icon { transform: rotate(-45deg); - font-size: 1.25em; - color: hsla(0, 0%, 0%, 0.7); + display: block; + font-size: 1.5em; +} + +.search-input { + border: none; + background-color: transparent; + outline: none; + color: inherit; +} + +.search-results { + border-top: 1px solid hsla(0, 0%, 0%, 0.2); +} + +.search-result { + list-style: none; + padding: 1em 1em; + cursor: pointer; +} + +.search-result:hover { + background-color: hsla(0, 0%, 0%, 0.05); +} + +.search-result:not(:last-child) { + border-bottom: 1px solid hsla(0, 0%, 0%, 0.1); +} + +.search-result-tag:not(:last-child) { + margin-bottom: 1em; } .parsha-books { diff --git a/cypress/integration/jumper.cypress.test.js b/cypress/integration/jumper.cypress.test.js index 979fe24d..eb4af772 100644 --- a/cypress/integration/jumper.cypress.test.js +++ b/cypress/integration/jumper.cypress.test.js @@ -76,13 +76,19 @@ describe('app', () => { cy.contains('וירא') }) - it.only('filters parshiyot', () => { + it('filters parshiyot', () => { cy.get('body').type('/') - cy - .get('[placeholder*="search" i]') + cy.contains(/vayishlach/i).should('not.be.visible') // shouldn't start by showing search results + + cy.focused() + .should('match', '[placeholder*="search" i]') .type('shlch') // vayishlach, b'shalach, sh'lach - cy.contains('נח').should('not.exist') + cy.contains('נח').should('not.be.visible') // hides parsha list + + cy.contains('Vayishlach') + cy.contains(`Beshalach`) + cy.contains(`Sh'lach`) }) }) diff --git a/dist/bundle.js b/dist/bundle.js index 51e6ce39..69177661 100644 --- a/dist/bundle.js +++ b/dist/bundle.js @@ -104,7 +104,7 @@ eval("module.exports = [[\"בראשית\"],[\"בראשית\"],[\"בראשית\"] /*! exports provided: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, default */ /***/ (function(module) { -eval("module.exports = [{\"he\":\"בראשית\",\"ref\":{\"b\":1,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"נח\",\"ref\":{\"b\":1,\"c\":6,\"v\":9,\"a\":[1]}},{\"he\":\"לך לך\",\"ref\":{\"b\":1,\"c\":12,\"v\":1,\"a\":[1]}},{\"he\":\"וירא\",\"ref\":{\"b\":1,\"c\":18,\"v\":1,\"a\":[1]}},{\"he\":\"חיי שרה\",\"ref\":{\"b\":1,\"c\":23,\"v\":1,\"a\":[1]}},{\"he\":\"תולדות\",\"ref\":{\"b\":1,\"c\":25,\"v\":19,\"a\":[1]}},{\"he\":\"ויצא\",\"ref\":{\"b\":1,\"c\":28,\"v\":10,\"a\":[1]}},{\"he\":\"וישלח\",\"ref\":{\"b\":1,\"c\":32,\"v\":4,\"a\":[1]}},{\"he\":\"וישב\",\"ref\":{\"b\":1,\"c\":37,\"v\":1,\"a\":[1]}},{\"he\":\"מקץ\",\"ref\":{\"b\":1,\"c\":41,\"v\":1,\"a\":[1]}},{\"he\":\"ויגש\",\"ref\":{\"b\":1,\"c\":44,\"v\":18,\"a\":[1]}},{\"he\":\"ויחי\",\"ref\":{\"b\":1,\"c\":47,\"v\":28,\"a\":[1]}},{\"he\":\"שמות\",\"ref\":{\"b\":2,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"וארא\",\"ref\":{\"b\":2,\"c\":6,\"v\":2,\"a\":[1]}},{\"he\":\"בא\",\"ref\":{\"b\":2,\"c\":10,\"v\":1,\"a\":[1]}},{\"he\":\"בשלח\",\"ref\":{\"b\":2,\"c\":13,\"v\":17,\"a\":[1]}},{\"he\":\"יתרו\",\"ref\":{\"b\":2,\"c\":18,\"v\":1,\"a\":[1]}},{\"he\":\"משפטים\",\"ref\":{\"b\":2,\"c\":21,\"v\":1,\"a\":[1]}},{\"he\":\"תרומה\",\"ref\":{\"b\":2,\"c\":25,\"v\":1,\"a\":[1]}},{\"he\":\"תצוה\",\"ref\":{\"b\":2,\"c\":27,\"v\":20,\"a\":[1]}},{\"he\":\"כי תשא\",\"ref\":{\"b\":2,\"c\":30,\"v\":11,\"a\":[1]}},{\"he\":\"ויקהל\",\"ref\":{\"b\":2,\"c\":35,\"v\":1,\"a\":[1]}},{\"he\":\"פקודי\",\"ref\":{\"b\":2,\"c\":38,\"v\":21,\"a\":[1]}},{\"he\":\"ויקרא\",\"ref\":{\"b\":3,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"צו\",\"ref\":{\"b\":3,\"c\":6,\"v\":1,\"a\":[1]}},{\"he\":\"שמיני\",\"ref\":{\"b\":3,\"c\":9,\"v\":1,\"a\":[1]}},{\"he\":\"תזריע\",\"ref\":{\"b\":3,\"c\":12,\"v\":1,\"a\":[1]}},{\"he\":\"מצורע\",\"ref\":{\"b\":3,\"c\":14,\"v\":1,\"a\":[1]}},{\"he\":\"אחרי מות\",\"ref\":{\"b\":3,\"c\":16,\"v\":1,\"a\":[1]}},{\"he\":\"קדושים\",\"ref\":{\"b\":3,\"c\":19,\"v\":1,\"a\":[1]}},{\"he\":\"אמור\",\"ref\":{\"b\":3,\"c\":21,\"v\":1,\"a\":[1]}},{\"he\":\"בהר\",\"ref\":{\"b\":3,\"c\":25,\"v\":1,\"a\":[1]}},{\"he\":\"בחקתי\",\"ref\":{\"b\":3,\"c\":26,\"v\":3,\"a\":[1]}},{\"he\":\"במדבר\",\"ref\":{\"b\":4,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"נשא\",\"ref\":{\"b\":4,\"c\":4,\"v\":21,\"a\":[1]}},{\"he\":\"בהעלותך\",\"ref\":{\"b\":4,\"c\":8,\"v\":1,\"a\":[1]}},{\"he\":\"שלח\",\"ref\":{\"b\":4,\"c\":13,\"v\":1,\"a\":[1]}},{\"he\":\"קרח\",\"ref\":{\"b\":4,\"c\":16,\"v\":1,\"a\":[1]}},{\"he\":\"חקת\",\"ref\":{\"b\":4,\"c\":19,\"v\":1,\"a\":[1]}},{\"he\":\"בלק\",\"ref\":{\"b\":4,\"c\":22,\"v\":2,\"a\":[1]}},{\"he\":\"פנחס\",\"ref\":{\"b\":4,\"c\":25,\"v\":10,\"a\":[1]}},{\"he\":\"מטות\",\"ref\":{\"b\":4,\"c\":30,\"v\":2,\"a\":[1]}},{\"he\":\"מסעי\",\"ref\":{\"b\":4,\"c\":33,\"v\":1,\"a\":[1]}},{\"he\":\"דברים\",\"ref\":{\"b\":5,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"ואתחנן\",\"ref\":{\"b\":5,\"c\":3,\"v\":23,\"a\":[1]}},{\"he\":\"עקב\",\"ref\":{\"b\":5,\"c\":7,\"v\":12,\"a\":[1]}},{\"he\":\"ראה\",\"ref\":{\"b\":5,\"c\":11,\"v\":26,\"a\":[1]}},{\"he\":\"שופטים\",\"ref\":{\"b\":5,\"c\":16,\"v\":18,\"a\":[1]}},{\"he\":\"כי תצא\",\"ref\":{\"b\":5,\"c\":21,\"v\":10,\"a\":[1]}},{\"he\":\"כי תבוא\",\"ref\":{\"b\":5,\"c\":26,\"v\":1,\"a\":[1]}},{\"he\":\"נצבים\",\"ref\":{\"b\":5,\"c\":29,\"v\":9,\"a\":[1]}},{\"he\":\"וילך\",\"ref\":{\"b\":5,\"c\":31,\"v\":1,\"a\":[1]}},{\"he\":\"האזינו\",\"ref\":{\"b\":5,\"c\":32,\"v\":1,\"a\":[1]}},{\"he\":\"וזאת הברכה\",\"ref\":{\"b\":5,\"c\":33,\"v\":1,\"a\":[1]}}];\n\n//# sourceURL=webpack:///./build/parshiyot.json?"); +eval("module.exports = [{\"he\":\"בראשית\",\"en\":\"Bereshit\",\"ref\":{\"b\":1,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"נח\",\"en\":\"Noach\",\"ref\":{\"b\":1,\"c\":6,\"v\":9,\"a\":[1]}},{\"he\":\"לך לך\",\"en\":\"Lech Lecha\",\"ref\":{\"b\":1,\"c\":12,\"v\":1,\"a\":[1]}},{\"he\":\"וירא\",\"en\":\"Vayera\",\"ref\":{\"b\":1,\"c\":18,\"v\":1,\"a\":[1]}},{\"he\":\"חיי שרה\",\"en\":\"Chayei Sara\",\"ref\":{\"b\":1,\"c\":23,\"v\":1,\"a\":[1]}},{\"he\":\"תולדות\",\"en\":\"Toldot\",\"ref\":{\"b\":1,\"c\":25,\"v\":19,\"a\":[1]}},{\"he\":\"ויצא\",\"en\":\"Vayetzei\",\"ref\":{\"b\":1,\"c\":28,\"v\":10,\"a\":[1]}},{\"he\":\"וישלח\",\"en\":\"Vayishlach\",\"ref\":{\"b\":1,\"c\":32,\"v\":4,\"a\":[1]}},{\"he\":\"וישב\",\"en\":\"Vayeshev\",\"ref\":{\"b\":1,\"c\":37,\"v\":1,\"a\":[1]}},{\"he\":\"מקץ\",\"en\":\"Miketz\",\"ref\":{\"b\":1,\"c\":41,\"v\":1,\"a\":[1]}},{\"he\":\"ויגש\",\"en\":\"Vayigash\",\"ref\":{\"b\":1,\"c\":44,\"v\":18,\"a\":[1]}},{\"he\":\"ויחי\",\"en\":\"Vayechi\",\"ref\":{\"b\":1,\"c\":47,\"v\":28,\"a\":[1]}},{\"he\":\"שמות\",\"en\":\"Shemot\",\"ref\":{\"b\":2,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"וארא\",\"en\":\"Vaera\",\"ref\":{\"b\":2,\"c\":6,\"v\":2,\"a\":[1]}},{\"he\":\"בא\",\"en\":\"Bo\",\"ref\":{\"b\":2,\"c\":10,\"v\":1,\"a\":[1]}},{\"he\":\"בשלח\",\"en\":\"Beshalach\",\"ref\":{\"b\":2,\"c\":13,\"v\":17,\"a\":[1]}},{\"he\":\"יתרו\",\"en\":\"Yitro\",\"ref\":{\"b\":2,\"c\":18,\"v\":1,\"a\":[1]}},{\"he\":\"משפטים\",\"en\":\"Mishpatim\",\"ref\":{\"b\":2,\"c\":21,\"v\":1,\"a\":[1]}},{\"he\":\"תרומה\",\"en\":\"Terumah\",\"ref\":{\"b\":2,\"c\":25,\"v\":1,\"a\":[1]}},{\"he\":\"תצוה\",\"en\":\"Tetzaveh\",\"ref\":{\"b\":2,\"c\":27,\"v\":20,\"a\":[1]}},{\"he\":\"כי תשא\",\"en\":\"Ki Tisa\",\"ref\":{\"b\":2,\"c\":30,\"v\":11,\"a\":[1]}},{\"he\":\"ויקהל\",\"en\":\"Vayakhel\",\"ref\":{\"b\":2,\"c\":35,\"v\":1,\"a\":[1]}},{\"he\":\"פקודי\",\"en\":\"Pekudei\",\"ref\":{\"b\":2,\"c\":38,\"v\":21,\"a\":[1]}},{\"he\":\"ויקרא\",\"en\":\"Vayikra\",\"ref\":{\"b\":3,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"צו\",\"en\":\"Tzav\",\"ref\":{\"b\":3,\"c\":6,\"v\":1,\"a\":[1]}},{\"he\":\"שמיני\",\"en\":\"Shmini\",\"ref\":{\"b\":3,\"c\":9,\"v\":1,\"a\":[1]}},{\"he\":\"תזריע\",\"en\":\"Tazria\",\"ref\":{\"b\":3,\"c\":12,\"v\":1,\"a\":[1]}},{\"he\":\"מצורע\",\"en\":\"Metzora\",\"ref\":{\"b\":3,\"c\":14,\"v\":1,\"a\":[1]}},{\"he\":\"אחרי מות\",\"en\":\"Achrei Mot\",\"ref\":{\"b\":3,\"c\":16,\"v\":1,\"a\":[1]}},{\"he\":\"קדושים\",\"en\":\"Kedoshim\",\"ref\":{\"b\":3,\"c\":19,\"v\":1,\"a\":[1]}},{\"he\":\"אמור\",\"en\":\"Emor\",\"ref\":{\"b\":3,\"c\":21,\"v\":1,\"a\":[1]}},{\"he\":\"בהר\",\"en\":\"Behar\",\"ref\":{\"b\":3,\"c\":25,\"v\":1,\"a\":[1]}},{\"he\":\"בחקתי\",\"en\":\"Bechukotai\",\"ref\":{\"b\":3,\"c\":26,\"v\":3,\"a\":[1]}},{\"he\":\"במדבר\",\"en\":\"Bamidbar\",\"ref\":{\"b\":4,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"נשא\",\"en\":\"Nasso\",\"ref\":{\"b\":4,\"c\":4,\"v\":21,\"a\":[1]}},{\"he\":\"בהעלותך\",\"en\":\"Beha'alotcha\",\"ref\":{\"b\":4,\"c\":8,\"v\":1,\"a\":[1]}},{\"he\":\"שלח\",\"en\":\"Sh'lach\",\"ref\":{\"b\":4,\"c\":13,\"v\":1,\"a\":[1]}},{\"he\":\"קרח\",\"en\":\"Korach\",\"ref\":{\"b\":4,\"c\":16,\"v\":1,\"a\":[1]}},{\"he\":\"חקת\",\"en\":\"Chukat\",\"ref\":{\"b\":4,\"c\":19,\"v\":1,\"a\":[1]}},{\"he\":\"בלק\",\"en\":\"Balak\",\"ref\":{\"b\":4,\"c\":22,\"v\":2,\"a\":[1]}},{\"he\":\"פנחס\",\"en\":\"Pinchas\",\"ref\":{\"b\":4,\"c\":25,\"v\":10,\"a\":[1]}},{\"he\":\"מטות\",\"en\":\"Matot\",\"ref\":{\"b\":4,\"c\":30,\"v\":2,\"a\":[1]}},{\"he\":\"מסעי\",\"en\":\"Masei\",\"ref\":{\"b\":4,\"c\":33,\"v\":1,\"a\":[1]}},{\"he\":\"דברים\",\"en\":\"Devarim\",\"ref\":{\"b\":5,\"c\":1,\"v\":1,\"a\":[1]}},{\"he\":\"ואתחנן\",\"en\":\"Vaetchanan\",\"ref\":{\"b\":5,\"c\":3,\"v\":23,\"a\":[1]}},{\"he\":\"עקב\",\"en\":\"Eikev\",\"ref\":{\"b\":5,\"c\":7,\"v\":12,\"a\":[1]}},{\"he\":\"ראה\",\"en\":\"Re'eh\",\"ref\":{\"b\":5,\"c\":11,\"v\":26,\"a\":[1]}},{\"he\":\"שופטים\",\"en\":\"Shoftim\",\"ref\":{\"b\":5,\"c\":16,\"v\":18,\"a\":[1]}},{\"he\":\"כי תצא\",\"en\":\"Ki Teitzei\",\"ref\":{\"b\":5,\"c\":21,\"v\":10,\"a\":[1]}},{\"he\":\"כי תבוא\",\"en\":\"Ki Tavo\",\"ref\":{\"b\":5,\"c\":26,\"v\":1,\"a\":[1]}},{\"he\":\"נצבים\",\"en\":\"Nitzavim\",\"ref\":{\"b\":5,\"c\":29,\"v\":9,\"a\":[1]}},{\"he\":\"וילך\",\"en\":\"Vayeilech\",\"ref\":{\"b\":5,\"c\":31,\"v\":1,\"a\":[1]}},{\"he\":\"האזינו\",\"en\":\"Ha'Azinu\",\"ref\":{\"b\":5,\"c\":32,\"v\":1,\"a\":[1]}},{\"he\":\"וזאת הברכה\",\"en\":\"Vezot Haberakhah\",\"ref\":{\"b\":5,\"c\":33,\"v\":1,\"a\":[1]}}];\n\n//# sourceURL=webpack:///./build/parshiyot.json?"); /***/ }), @@ -147,11 +147,11 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Lin /*!************************************!*\ !*** ./components/ParshaPicker.js ***! \************************************/ -/*! exports provided: default */ +/*! exports provided: search, default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../build/parshiyot.json */ \"./build/parshiyot.json\");\nvar _build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0___namespace = /*#__PURE__*/__webpack_require__.t(/*! ../build/parshiyot.json */ \"./build/parshiyot.json\", 1);\n\n\nconst Parsha = ({ ref, he }) => `\n ${he}\n\n`\n\nconst Book = (book) => `\n
  • \n
      \n ${book.map(Parsha).join('')}\n
    \n
  • \n`\n\nconst ParshaPicker = () => `\n
    \n
    \n \n \n
    \n
      \n ${_build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0__\n .reduce((books, parsha) => {\n const book = parsha.ref.b\n books[book] = books[book] || []\n books[book].push(parsha)\n return books\n }, [])\n .map(Book)\n .join('')\n }\n
    \n
    \n`\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (ParshaPicker);\n\n\n//# sourceURL=webpack:///./components/ParshaPicker.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"search\", function() { return search; });\n/* harmony import */ var _build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../build/parshiyot.json */ \"./build/parshiyot.json\");\nvar _build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0___namespace = /*#__PURE__*/__webpack_require__.t(/*! ../build/parshiyot.json */ \"./build/parshiyot.json\", 1);\n/* harmony import */ var _src_fuzzy__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../src/fuzzy */ \"./src/fuzzy.js\");\n/* harmony import */ var _src_fuzzy__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_src_fuzzy__WEBPACK_IMPORTED_MODULE_1__);\n\n\n\nconst Parsha = ({ ref, he }) => `\n ${he}\n\n`\n\nconst Book = (book) => `\n
  • \n
      \n ${book.map(Parsha).join('')}\n
    \n
  • \n`\n\n/*\n
  • \n

    שלח

    \n

    Shelach

    \n
  • \n
  • \n

    בשלח

    \n

    Beshalach

    \n
  • \n
  • \n

    וישלח

    \n

    Vayishlach

    \n
  • \n*/\n\n/*\n
  • \n

    No results

    \n
  • \n*/\nconst Search = () => `\n
    \n
    \n \n \n
    \n
      \n
    \n
    \n`\n\nconst decorateString = ({ string, atIndexes, withDecoration }) => {\n let indexesIndex = 0\n return string\n .split('')\n .map((char, i) => {\n if (atIndexes[indexesIndex] !== i) return char\n\n ++indexesIndex\n return withDecoration(char)\n }, '')\n .join('')\n}\n\nconst SearchResult = ({ string, indexes }) => `\n
  • \n

    ${decorateString({\n string,\n atIndexes: indexes,\n withDecoration: (c) => (`${c}`)\n })}\n

    \n
  • \n`\n\nconst ParshaPicker = () => `\n
    \n ${Search()}\n
      \n ${_build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0__\n .reduce((books, parsha) => {\n const book = parsha.ref.b\n books[book] = books[book] || []\n books[book].push(parsha)\n return books\n }, [])\n .map(Book)\n .join('')\n }\n
    \n
    \n`\n\nconst searchResults = (query) => _src_fuzzy__WEBPACK_IMPORTED_MODULE_1___default()(_build_parshiyot_json__WEBPACK_IMPORTED_MODULE_0__.map(parsha => parsha.en), query)\n\nconst htmlToElement = (html) => {\n const template = document.createElement('template')\n html = html.trim() // Never return a text node of whitespace as the result\n template.innerHTML = html\n return template.content.firstChild\n}\n\nconst top = (n) => (_, i) => i < n\n\nconst search = ({ jumper, query }) => {\n const searchResultsElement = jumper.querySelector('.search-results')\n\n if (query) {\n searchResultsElement.classList.remove('u-hidden')\n jumper.querySelector('.parsha-books').classList.add('u-hidden')\n\n searchResultsElement.innerHTML = ''\n\n searchResults(query)\n .filter(top(5))\n .map(SearchResult)\n .map(htmlToElement)\n .forEach(result => {\n searchResultsElement.appendChild(result)\n })\n } else {\n searchResultsElement.classList.add('u-hidden')\n jumper.querySelector('.parsha-books').classList.remove('u-hidden')\n }\n}\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (ParshaPicker);\n\n\n//# sourceURL=webpack:///./components/ParshaPicker.js?"); /***/ }), @@ -163,7 +163,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _bui /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src */ \"./src/index.js\");\n/* harmony import */ var _components_Page__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/Page */ \"./components/Page.js\");\n/* harmony import */ var _components_ParshaPicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/ParshaPicker */ \"./components/ParshaPicker.js\");\n/* harmony import */ var _build_page_titles_json__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./build/page-titles.json */ \"./build/page-titles.json\");\nvar _build_page_titles_json__WEBPACK_IMPORTED_MODULE_3___namespace = /*#__PURE__*/__webpack_require__.t(/*! ./build/page-titles.json */ \"./build/page-titles.json\", 1);\n\n\n\n\n\nconst insertBefore = (parent, child) => {\n parent.insertAdjacentElement('afterbegin', child)\n}\n\nconst insertAfter = (parent, child) => {\n parent.insertAdjacentElement('beforeend', child)\n}\n\nconst fetchPage = (n) => {\n if (n <= 0) return Promise.resolve({})\n\n return window.fetch(`/build/pages/${n}.json`)\n .then((res) => res.json())\n .then((page) => ({ key: n, content: page }))\n .catch((err) => {\n console.error(err)\n return {}\n })\n}\n\nconst iterator = _src__WEBPACK_IMPORTED_MODULE_0__[\"IntegerIterator\"].new({ startingAt: 1 })\n\nconst cache = {}\n\nconst unpackCache = (cache) => Object.keys(cache).map(key => cache[key])\n\nlet isShowingParshaPicker = false\n\nconst htmlToElement = (html) => {\n const template = document.createElement('template')\n html = html.trim() // Never return a text node of whitespace as the result\n template.innerHTML = html\n return template.content.firstChild\n}\n\nconst state = {\n iterator\n}\n\nconst render = ({ cache, showAnnotations, title }) => {\n unpackCache(cache)\n .forEach(({ node, content }) => {\n const el = htmlToElement(Object(_components_Page__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(content, showAnnotations))\n\n const firstChild = node.firstChild\n if (firstChild) {\n node.replaceChild(el, firstChild)\n } else {\n node.appendChild(el)\n }\n })\n\n document.querySelector('[data-target-id=\"parsha-title\"]').innerHTML = title\n}\n\nconst setState = (updates) => {\n const newState = Object.assign(state, updates)\n\n render(newState)\n}\n\nconst purgeObject = (obj) => {\n for (const key in obj) {\n delete obj[key]\n }\n}\n\nconst purgeNode = (node) => {\n while (node.firstChild) node.removeChild(node.firstChild)\n}\n\nconst makePageNode = (n) => {\n const node = document.createElement('div')\n node.classList.add('tikkun-page')\n node.setAttribute('data-page-number', n)\n\n return node\n}\n\nconst scrollToLine = ({ node, lineIndex }) => {\n const lines = [...node.querySelectorAll('.line')]\n\n const line = lines[lineIndex]\n\n const book = document.querySelector('.tikkun-book')\n\n book.scrollTop = line.offsetTop + (line.offsetHeight / 2) - (book.offsetHeight / 2)\n}\n\nconst app = {\n jumpTo: ({ ref }) => {\n purgeObject(cache)\n\n const { pageNumber, lineNumber } = Object(_src__WEBPACK_IMPORTED_MODULE_0__[\"physicalLocationFromRef\"])(ref)\n\n state.iterator = _src__WEBPACK_IMPORTED_MODULE_0__[\"IntegerIterator\"].new({ startingAt: pageNumber })\n\n purgeNode(document.querySelector('[data-target-id=\"tikkun-book\"]'))\n\n fetchPage(state.iterator.next())\n .then(renderNext)\n .then((pageNode) => {\n scrollToLine({ node: pageNode, lineIndex: lineNumber - 1 })\n })\n }\n}\n\nconst showParshaPicker = () => {\n document.querySelector('#js-app').appendChild(htmlToElement(Object(_components_ParshaPicker__WEBPACK_IMPORTED_MODULE_2__[\"default\"])()))\n\n document.querySelector('.parsha-search-input').addEventListener('input', (e) => {\n console.log(e.target.value)\n })\n\n ;[...document.querySelectorAll('[data-target-id=\"parsha\"]')]\n .forEach((parsha) => {\n parsha.addEventListener('click', (e) => {\n const refPart = (part) => Number(e.target.getAttribute(`data-jump-to-${part}`))\n app.jumpTo({ ref: { b: refPart('book'), c: refPart('chapter'), v: refPart('verse') } })\n\n toggleParshaPicker()\n })\n })\n}\n\nconst toggleParshaPicker = () => {\n isShowingParshaPicker = !isShowingParshaPicker\n\n ;[\n '[data-test-id=\"annotations-toggle\"]',\n '[data-target-id=\"repo-link\"]',\n '[data-target-id=\"tikkun-book\"]'\n ]\n .map(selector => document.querySelector(selector))\n .map(el => el.classList)\n .forEach(classList => {\n classList.toggle('u-hidden')\n classList.toggle('mod-animated')\n })\n\n if (isShowingParshaPicker) {\n showParshaPicker()\n } else {\n document.querySelector('#js-app').removeChild(document.querySelector('.parsha-picker'))\n }\n}\n\nconst toggleAnnotations = () => {\n const toggle = document.querySelector('[data-target-id=\"annotations-toggle\"]')\n\n toggle.checked = !toggle.checked\n\n setState({ showAnnotations: toggle.checked })\n}\n\nconst isInView = (view, scrollView) => {\n return (\n view.offsetTop < scrollView.scrollTop + scrollView.clientHeight\n ) && (\n view.offsetTop + view.clientHeight > scrollView.scrollTop\n )\n}\n\nconst updatePageTitle = (scrollView) => {\n const pages = [...document.querySelectorAll('.tikkun-page')]\n\n const inViewPages = pages.filter((page) => isInView(page, scrollView))\n\n const firstPageInView = inViewPages[0]\n\n const n = Number(firstPageInView.getAttribute('data-page-number'))\n setState({ title: Object(_src__WEBPACK_IMPORTED_MODULE_0__[\"title\"])(_build_page_titles_json__WEBPACK_IMPORTED_MODULE_3__[n - 1]) })\n}\n\nlet timeout\nconst debounce = (f) => {\n clearTimeout(timeout)\n timeout = setTimeout(f, 100)\n}\n\nconst renderPage = ({ insertStrategy: insert }) => ({ key, content }) => {\n const node = makePageNode(key)\n\n cache[key] = { node, content }\n insert(document.querySelector('[data-target-id=\"tikkun-book\"]'), node)\n\n setState({\n cache,\n showAnnotations: document.querySelector('[data-target-id=\"annotations-toggle\"]').checked,\n title: Object(_src__WEBPACK_IMPORTED_MODULE_0__[\"title\"])(_build_page_titles_json__WEBPACK_IMPORTED_MODULE_3__[key - 1])\n })\n\n return node\n}\n\nconst renderPrevious = renderPage({ insertStrategy: insertBefore })\nconst renderNext = renderPage({ insertStrategy: insertAfter })\n\nconst whenKey = (key, callback) => e => {\n if (e.key === key) callback()\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n _src__WEBPACK_IMPORTED_MODULE_0__[\"InfiniteScroller\"]\n .new({\n container: document.querySelector('[data-target-id=\"tikkun-book\"]'),\n fetchPreviousContent: {\n fetch: () => fetchPage(state.iterator.previous()),\n render: (container, { key, content }) => renderPrevious({ key, content })\n },\n fetchNextContent: {\n fetch: () => fetchPage(state.iterator.next()),\n render: (container, { key, content }) => renderNext({ key, content })\n }\n })\n .attach()\n\n document.querySelector('[data-target-id=\"tikkun-book\"]').addEventListener('scroll', (e) => {\n debounce(() => updatePageTitle(e.target))\n })\n\n document.querySelector('[data-target-id=\"annotations-toggle\"]').addEventListener('change', (e) => {\n const showAnnotations = e.target.checked\n\n setState({ showAnnotations })\n })\n\n document.addEventListener('keydown', whenKey('Shift', toggleAnnotations))\n document.addEventListener('keyup', whenKey('Shift', toggleAnnotations))\n\n document.querySelector('[data-target-id=\"parsha-title\"]').addEventListener('click', toggleParshaPicker)\n document.addEventListener('keydown', whenKey('/', toggleParshaPicker))\n\n fetchPage(state.iterator.next())\n .then(renderNext)\n})\n\n\n//# sourceURL=webpack:///./index.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src */ \"./src/index.js\");\n/* harmony import */ var _components_Page__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/Page */ \"./components/Page.js\");\n/* harmony import */ var _components_ParshaPicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/ParshaPicker */ \"./components/ParshaPicker.js\");\n/* harmony import */ var _build_page_titles_json__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./build/page-titles.json */ \"./build/page-titles.json\");\nvar _build_page_titles_json__WEBPACK_IMPORTED_MODULE_3___namespace = /*#__PURE__*/__webpack_require__.t(/*! ./build/page-titles.json */ \"./build/page-titles.json\", 1);\n\n\n\n\n\nconst insertBefore = (parent, child) => {\n parent.insertAdjacentElement('afterbegin', child)\n}\n\nconst insertAfter = (parent, child) => {\n parent.insertAdjacentElement('beforeend', child)\n}\n\nconst fetchPage = (n) => {\n if (n <= 0) return Promise.resolve({})\n\n return window.fetch(`/build/pages/${n}.json`)\n .then((res) => res.json())\n .then((page) => ({ key: n, content: page }))\n .catch((err) => {\n console.error(err)\n return {}\n })\n}\n\nconst iterator = _src__WEBPACK_IMPORTED_MODULE_0__[\"IntegerIterator\"].new({ startingAt: 1 })\n\nconst cache = {}\n\nconst unpackCache = (cache) => Object.keys(cache).map(key => cache[key])\n\nlet isShowingParshaPicker = false\n\nconst htmlToElement = (html) => {\n const template = document.createElement('template')\n html = html.trim() // Never return a text node of whitespace as the result\n template.innerHTML = html\n return template.content.firstChild\n}\n\nconst state = {\n iterator\n}\n\nconst render = ({ cache, showAnnotations, title }) => {\n unpackCache(cache)\n .forEach(({ node, content }) => {\n const el = htmlToElement(Object(_components_Page__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(content, showAnnotations))\n\n const firstChild = node.firstChild\n if (firstChild) {\n node.replaceChild(el, firstChild)\n } else {\n node.appendChild(el)\n }\n })\n\n document.querySelector('[data-target-id=\"parsha-title\"]').innerHTML = title\n}\n\nconst setState = (updates) => {\n const newState = Object.assign(state, updates)\n\n render(newState)\n}\n\nconst purgeObject = (obj) => {\n for (const key in obj) {\n delete obj[key]\n }\n}\n\nconst purgeNode = (node) => {\n while (node.firstChild) node.removeChild(node.firstChild)\n}\n\nconst makePageNode = (n) => {\n const node = document.createElement('div')\n node.classList.add('tikkun-page')\n node.setAttribute('data-page-number', n)\n\n return node\n}\n\nconst scrollToLine = ({ node, lineIndex }) => {\n const lines = [...node.querySelectorAll('.line')]\n\n const line = lines[lineIndex]\n\n const book = document.querySelector('.tikkun-book')\n\n book.scrollTop = line.offsetTop + (line.offsetHeight / 2) - (book.offsetHeight / 2)\n}\n\nconst app = {\n jumpTo: ({ ref }) => {\n purgeObject(cache)\n\n const { pageNumber, lineNumber } = Object(_src__WEBPACK_IMPORTED_MODULE_0__[\"physicalLocationFromRef\"])(ref)\n\n state.iterator = _src__WEBPACK_IMPORTED_MODULE_0__[\"IntegerIterator\"].new({ startingAt: pageNumber })\n\n purgeNode(document.querySelector('[data-target-id=\"tikkun-book\"]'))\n\n fetchPage(state.iterator.next())\n .then(renderNext)\n .then((pageNode) => {\n scrollToLine({ node: pageNode, lineIndex: lineNumber - 1 })\n })\n }\n}\n\nconst showParshaPicker = () => {\n const jumper = htmlToElement(Object(_components_ParshaPicker__WEBPACK_IMPORTED_MODULE_2__[\"default\"])())\n document.querySelector('#js-app').appendChild(jumper)\n\n jumper.querySelector('.search-input').addEventListener('input', (e) => {\n Object(_components_ParshaPicker__WEBPACK_IMPORTED_MODULE_2__[\"search\"])({ jumper, query: e.target.value })\n })\n\n ;[...document.querySelectorAll('[data-target-id=\"parsha\"]')]\n .forEach((parsha) => {\n parsha.addEventListener('click', (e) => {\n const refPart = (part) => Number(e.target.getAttribute(`data-jump-to-${part}`))\n app.jumpTo({ ref: { b: refPart('book'), c: refPart('chapter'), v: refPart('verse') } })\n\n toggleParshaPicker()\n })\n })\n}\n\nconst toggleParshaPicker = () => {\n isShowingParshaPicker = !isShowingParshaPicker\n\n ;[\n '[data-test-id=\"annotations-toggle\"]',\n '[data-target-id=\"repo-link\"]',\n '[data-target-id=\"tikkun-book\"]'\n ]\n .map(selector => document.querySelector(selector))\n .map(el => el.classList)\n .forEach(classList => {\n classList.toggle('u-hidden')\n classList.toggle('mod-animated')\n })\n\n if (isShowingParshaPicker) {\n showParshaPicker()\n } else {\n document.querySelector('#js-app').removeChild(document.querySelector('.parsha-picker'))\n }\n}\n\nconst toggleAnnotations = () => {\n const toggle = document.querySelector('[data-target-id=\"annotations-toggle\"]')\n\n toggle.checked = !toggle.checked\n\n setState({ showAnnotations: toggle.checked })\n}\n\nconst isInView = (view, scrollView) => {\n return (\n view.offsetTop < scrollView.scrollTop + scrollView.clientHeight\n ) && (\n view.offsetTop + view.clientHeight > scrollView.scrollTop\n )\n}\n\nconst updatePageTitle = (scrollView) => {\n const pages = [...document.querySelectorAll('.tikkun-page')]\n\n const inViewPages = pages.filter((page) => isInView(page, scrollView))\n\n const firstPageInView = inViewPages[0]\n\n const n = Number(firstPageInView.getAttribute('data-page-number'))\n setState({ title: Object(_src__WEBPACK_IMPORTED_MODULE_0__[\"title\"])(_build_page_titles_json__WEBPACK_IMPORTED_MODULE_3__[n - 1]) })\n}\n\nlet timeout\nconst debounce = (f) => {\n clearTimeout(timeout)\n timeout = setTimeout(f, 100)\n}\n\nconst renderPage = ({ insertStrategy: insert }) => ({ key, content }) => {\n const node = makePageNode(key)\n\n cache[key] = { node, content }\n insert(document.querySelector('[data-target-id=\"tikkun-book\"]'), node)\n\n setState({\n cache,\n showAnnotations: document.querySelector('[data-target-id=\"annotations-toggle\"]').checked,\n title: Object(_src__WEBPACK_IMPORTED_MODULE_0__[\"title\"])(_build_page_titles_json__WEBPACK_IMPORTED_MODULE_3__[key - 1])\n })\n\n return node\n}\n\nconst renderPrevious = renderPage({ insertStrategy: insertBefore })\nconst renderNext = renderPage({ insertStrategy: insertAfter })\n\nconst whenKey = (key, callback) => e => {\n if (e.key === key) callback()\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n _src__WEBPACK_IMPORTED_MODULE_0__[\"InfiniteScroller\"]\n .new({\n container: document.querySelector('[data-target-id=\"tikkun-book\"]'),\n fetchPreviousContent: {\n fetch: () => fetchPage(state.iterator.previous()),\n render: (container, { key, content }) => renderPrevious({ key, content })\n },\n fetchNextContent: {\n fetch: () => fetchPage(state.iterator.next()),\n render: (container, { key, content }) => renderNext({ key, content })\n }\n })\n .attach()\n\n document.querySelector('[data-target-id=\"tikkun-book\"]').addEventListener('scroll', (e) => {\n debounce(() => updatePageTitle(e.target))\n })\n\n document.querySelector('[data-target-id=\"annotations-toggle\"]').addEventListener('change', (e) => {\n const showAnnotations = e.target.checked\n\n setState({ showAnnotations })\n })\n\n document.addEventListener('keydown', whenKey('Shift', toggleAnnotations))\n document.addEventListener('keyup', whenKey('Shift', toggleAnnotations))\n\n document.querySelector('[data-target-id=\"parsha-title\"]').addEventListener('click', toggleParshaPicker)\n document.addEventListener('keydown', whenKey('/', toggleParshaPicker))\n\n fetchPage(state.iterator.next())\n .then(renderNext)\n // .then(toggleParshaPicker)\n})\n\n\n//# sourceURL=webpack:///./index.js?"); /***/ }), @@ -178,6 +178,17 @@ eval("const hebrewNumeralFromInteger = __webpack_require__(/*! ./hebrew-numeral /***/ }), +/***/ "./src/fuzzy.js": +/*!**********************!*\ + !*** ./src/fuzzy.js ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("const hasEveryCharacterInOrder = needle => item => (new RegExp(needle\n .split('')\n .join('.*')\n, 'i')).test(item)\n\nconst matchIndexes = (needle, match) => {\n const needleChars = needle.split('')\n const matchChars = match.split('')\n\n const indexes = []\n let needleIndex = 0\n\n for (let i = 0; i < matchChars.length; i++) {\n if (needleChars[needleIndex].toLowerCase() === matchChars[i].toLowerCase()) {\n indexes.push(i)\n ++needleIndex\n\n if (needleIndex >= needleChars.length) break\n }\n }\n\n return indexes\n}\n\nconst indexScore = (needle, match) => {\n const indexes = matchIndexes(needle, match)\n\n return indexes\n .map(index => index - indexes[0])\n .reduce((a, b) => a + b)\n}\n\nmodule.exports = (haystack, needle) => haystack\n .filter(hasEveryCharacterInOrder(needle))\n .map(match => ({ indexes: matchIndexes(needle, match), string: match }))\n .sort((match, other) => {\n const matchScore = indexScore(needle, match.string)\n const otherScore = indexScore(needle, other.string)\n\n const score = matchScore - otherScore\n\n if (score === 0) {\n return match.indexes[0] - other.indexes[0]\n }\n\n return score\n })\n\n\n//# sourceURL=webpack:///./src/fuzzy.js?"); + +/***/ }), + /***/ "./src/hebrew-numeral.js": /*!*******************************!*\ !*** ./src/hebrew-numeral.js ***! diff --git a/index.js b/index.js index 15fbc393..cdea0567 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ import { InfiniteScroller, IntegerIterator, title as getTitle, physicalLocationFromRef } from './src' import Page from './components/Page' -import ParshaPicker from './components/ParshaPicker' +import ParshaPicker, { search } from './components/ParshaPicker' import pageTitles from './build/page-titles.json' const insertBefore = (parent, child) => { @@ -111,10 +111,11 @@ const app = { } const showParshaPicker = () => { - document.querySelector('#js-app').appendChild(htmlToElement(ParshaPicker())) + const jumper = htmlToElement(ParshaPicker()) + document.querySelector('#js-app').appendChild(jumper) - document.querySelector('.parsha-search-input').addEventListener('input', (e) => { - console.log(e.target.value) + jumper.querySelector('.search-input').addEventListener('input', (e) => { + search({ jumper, query: e.target.value }) }) ;[...document.querySelectorAll('[data-target-id="parsha"]')] @@ -238,4 +239,5 @@ document.addEventListener('DOMContentLoaded', () => { fetchPage(state.iterator.next()) .then(renderNext) + // .then(toggleParshaPicker) })