switch to vite

This commit is contained in:
adueck 2024-08-12 15:48:31 -04:00
parent c5a238ab05
commit b4bee62036
186 changed files with 8298 additions and 60694 deletions

1
.env
View File

@ -1 +0,0 @@
SKIP_PREFLIGHT_CHECK=true

View File

@ -18,12 +18,13 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
cache: "yarn"
- name: Install, build, test
run: |
yarn install-r
yarn build-library
yarn install-all
yarn build-lib
yarn build-components
yarn build-website
yarn test --silent
yarn check-all-inflections

View File

@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
cache: "yarn"
- name: Check if version has been updated
id: check
@ -27,10 +27,10 @@ jobs:
- name: Publish when version changed
if: steps.check.outputs.changed == 'true'
run: |
yarn install-r
yarn build-library
yarn install-all
yarn build-lib
yarn build-components
yarn test --silent
yarn check-all-inflections
cp .npmrc src/lib
cp .npmrc src/components
cd src/lib

43
.gitignore vendored
View File

@ -1,12 +1,15 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# compiled library files
dist
# dependencies
node_modules
/.pnp
.pnp.js
# fetched vocab
src/verbs.ts
src/nouns-adjs.ts
# testing
/coverage
@ -14,20 +17,26 @@ node_modules
dict
diac.ts
# production
node_modules
build
dist
dist-ssr
*.local
/.pnp
.pnp.js
# misc
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
src/verbs.ts
src/nouns-adjs.ts
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

2
.npmrc
View File

@ -1,2 +0,0 @@
@lingdocs:registry=https://npm.lingdocs.com
//npm.lingdocs.com/:_authToken=${LINGDOCS_NPM_TOKEN}

2
.nvmrc
View File

@ -1 +1 @@
16
20

View File

@ -1,8 +1,5 @@
{
"typescript.preferences.autoImportFileExcludePatterns": [
"../../library.ts"
],
"cSpell.words": [
"کارخانه"
"library.ts"
],
}

View File

@ -20,7 +20,7 @@ This library uses a 3-step process to generate gramattically correct Pashto phra
| | Pashto Inflector Function | Chomskian Grammar Level |
|-|--------------------------| ----------------------- |
|1.| Assemble the phrase tree | Phrase Structure Rules |
|2.| Inflect the words in tree | Morphophonemic Rules |
|2.| Inflect the words in tree | Morphophonemic Rules |
|3.| Arrange the inflected words in order | Transformational Rules |
### 1. Assemble the phrase tree

View File

@ -2,7 +2,6 @@ import * as T from "./src/types";
import { inflectWord } from "./src/lib/src/pashto-inflector";
import * as tp from "./src/lib/src/type-predicates";
import { conjugateVerb } from "./src/lib/src/verb-conjugation";
import fetch from "node-fetch";
// Script to try inflecting all the words in the dictionary and make sure that
// no errors are thrown in the process
@ -15,8 +14,8 @@ type InflectionError = {
};
async function checkAll() {
console.log("Checking inflection functions on all dictionary words");
const res = await fetch(process.env.LINGDOCS_DICTIONARY_URL);
// @ts-ignore
const { entries }: T.Dictionary = await res.json();
const errors: InflectionError[] = [];

2
dev
View File

@ -1,2 +0,0 @@
nix-shell --command return
node --version

26
eslint.config.js Normal file
View File

@ -0,0 +1,26 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config({
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
ignores: ['dist'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
})

View File

@ -6,7 +6,6 @@
*
*/
const fs = require("fs");
const fetch = require("node-fetch-commonjs");
const path = require("path");
const verbCollectionPath = path.join(".", "vocab", "verbs");
const nounAdjCollectionPath = path.join(".", "vocab", "nouns-adjs");

1
global.d.ts vendored Normal file
View File

@ -0,0 +1 @@
import "jest-extended";

View File

@ -2,11 +2,11 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="An open source TypeScript/React library for Pashto inflection, verb conjugation, phrase generation, text conversion, and more" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<meta name="keywords" content="Pashto, Verbs, Conjugation, Grammar, Linguistics" />
<meta name="author" content="lingdocs.com" />
<link rel="canonical" href="https://pashto-inflector.lingdocs.com/" />
@ -25,10 +25,11 @@
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:creator" content="@lingdocs" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="manifest" href="/manifest.json" />
<title>Pashto Inflector</title>
</head>
<body class="d-flex flex-column h-100" id="root">
<noscript>You need to enable JavaScript to run this app.</noscript>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

13
jest.config.js Normal file
View File

@ -0,0 +1,13 @@
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
testEnvironment: "node",
transform: {
"^.+.tsx?$": [
"ts-jest",
{
tsconfig: "tsconfig.app.json",
},
],
},
setupFilesAfterEnv: ["./testSetup.ts"],
};

View File

@ -1,3 +1,3 @@
[build]
command = "yarn install-r && yarn test --silent && yarn build-website"
command = "yarn install-all && yarn test --silent && yarn build-website"
publish = "build/"

View File

@ -1,82 +1,48 @@
{
"name": "pashto-inflector",
"version": "7.5.1",
"author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com",
"license": "MIT",
"private": false,
"repository": {
"type": "git",
"url": "https://github.com/lingdocs/pashto-inflector.git"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.15.2",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.20",
"@types/node": "^15.12.1",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"bootstrap": "^4.6.0",
"jest-extended": "^4.0.1",
"node-fetch": "^3.3.2",
"node-fetch-commonjs": "^3.2.4",
"pbf": "^3.2.1",
"react": "^17.0.1",
"react-bootstrap": "^1.5.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.3",
"tsx": "^3.14.0",
"typescript": "^5.1.6",
"web-vitals": "^1.0.1"
},
"name": "minrep",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"patch": "npm version patch --no-git-tag-version && cd src/lib && npm version patch --no-git-tag-version && cd ../components && npm version patch --no-git-tag-version",
"minor": "npm version minor --no-git-tag-version && cd src/lib && npm version minor --no-git-tag-version && cd ../components && npm version minor --no-git-tag-version",
"major": "npm version major --no-git-tag-version && cd src/lib && npm version major --no-git-tag-version && cd ../components && npm version major --no-git-tag-version",
"preinstall": "echo '*** Be sure to use yarn install-r not yarn install ***!'",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"install-r": "yarn install && node get-words.js && cd src/lib && yarn install && cd ../components && yarn install",
"build-website": "node get-words.js && npm run build",
"build-library": "cd src/components && rimraf dist && tsc --project lib-tsconfig.json && node post-build.cjs && cd ../lib && rimraf dist && tsc --project lib-tsconfig.json && node_modules/rollup/dist/bin/rollup -c",
"test-ci": "npm run test -- --watchAll=false",
"get-words": "node get-words.js",
"preinstall": "echo '*** Be sure to use 'yarn install-all' not 'yarn install' ***!'",
"dev": "vite",
"lint": "eslint .",
"test": "jest",
"preview": "vite preview",
"install-all": "yarn install && node get-words.cjs $$ cd src/lib && yarn install && cd ../components && yarn install",
"build-website": "tsc -b && vite build",
"build-components": "rm -rf src/components/dist && tsc --project src/components/tsconfig.json && cd src/components && node post-build.cjs",
"build-lib": "rm -rf src/lib/dist && tsc --project src/lib/tsconfig.json",
"get-words": "node get-words.cjs",
"check-all-inflections": "tsx check-all-inflections.ts"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
"no-warning-comments": [
1,
{
"terms": [
"fixme",
"xxx"
],
"location": "anywhere"
}
]
}
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"bootstrap": "4.6.1",
"react-bootstrap": "1.5.1",
"@fortawesome/fontawesome-free": "^5.15.2"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"dependencies": {}
}
"devDependencies": {
"@eslint/js": "^9.8.0",
"@types/jest": "^29.5.12",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^9.8.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.9.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-extended": "^4.0.2",
"ts-jest": "^29.2.4",
"tsx": "^4.17.0",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0",
"vite": "^5.4.0"
}
}

View File

@ -1,10 +0,0 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.05";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShell {
packages = with pkgs; [
nodejs_20
];
}

View File

@ -8,7 +8,7 @@
import { useEffect, useState } from "react";
import ButtonSelect from "./components/src/ButtonSelect";
import ButtonSelect from "./components/src/selects/ButtonSelect";
import { Modal } from "react-bootstrap";
import * as T from "./types";
import defualtTextOptions from "./lib/src/default-text-options";
@ -36,6 +36,7 @@ function App() {
function handleHiderClick(label: string) {
setShowing((os) => (os === label ? "" : label));
}
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);

1
src/assets/react.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,2 +0,0 @@
storybook-static
node_modules

View File

@ -1,12 +0,0 @@
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": "@storybook/react"
}

View File

@ -1,16 +0,0 @@
// Bootstrap 4 and fontawesome 5 required for components css
import "bootstrap/dist/css/bootstrap.min.css";
import "@fortawesome/fontawesome-free/css/all.css";
// Plus some custom CSS
// TODO: this should be exported with the npm package!
import "../../App.css";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}

View File

@ -1,29 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"downlevelIteration": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "ES6",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"jsx": "react-jsx",
"outDir": "dist"
},
"files": [
"library.ts",
"images.d.ts"
]
}

View File

@ -1,80 +1,81 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import InflectionsTable from "./src/InflectionsTable";
import Pashto from "./src/Pashto";
import Phonetics from "./src/Phonetics";
import InlinePs from "./src/InlinePs";
import ButtonSelect from "./src/ButtonSelect";
import VerbFormDisplay from "./src/VerbFormDisplay";
import VerbTable from "./src/VerbTable";
import EPDisplay from "./src/ep-explorer/EPDisplay";
import Examples from "./src/Examples";
import Pashto from "./src/text-display/Pashto";
import Phonetics from "./src/text-display/Phonetics";
import InlinePs from "./src/text-display/InlinePs";
import Examples from "./src/text-display/Examples";
import CompiledPTextDisplay from "./src/text-display/CompiledPTextDisplay";
import ButtonSelect from "./src/selects/ButtonSelect";
import EntrySelect from "./src/selects/EntrySelect";
import PersonSelection from "./src/selects/PersonSelection";
import Hider from "./src/Hider";
import EntrySelect from "./src/EntrySelect";
import InflectionsTable from "./src/tables/InflectionsTable";
import VerbTable from "./src/tables/VerbTable";
import useStickyState from "./src/useStickyState";
import NPPicker from "./src/block-pickers/NPPicker";
import SandwichPicker from "./src/block-pickers/SandwichPicker";
import VerbFromDisplay from "./src/VerbFormDisplay";
import Block from "./src/blocks/Block";
import VerbInfo, { RootsAndStems } from "./src/verb-info/VerbInfo";
import VPExplorer from "./src/vp-explorer/VPExplorer";
import { makeVPSelectionState } from "../lib/src/phrase-building/verb-selection";
import { vpsReducer } from "../lib/src/phrase-building/vps-reducer";
import type { VpsReducerAction as VpsA } from "../lib/src/phrase-building/vps-reducer";
import useStickyState from "./src/useStickyState";
import Block, { NPBlock, APBlock } from "./src/blocks/Block";
import { roleIcon } from "./src/vp-explorer/VPExplorerExplanationModal";
import CompiledPTextDisplay from "./src/CompiledPTextDisplay";
import RenderedBlocksDisplay from "./src/RenderedBlocksDisplay";
import NPPicker from "./src/np-picker/NPPicker";
import EPPicker from "./src/ep-explorer/EPPicker";
import EPExplorer from "./src/ep-explorer/EPExplorer";
import APPicker from "./src/ap-picker/APPicker";
import playAudio from "./src/play-audio";
import { roleIcon } from "./src/role-icons";
import { vpsReducer } from "../lib/src/phrase-building/vps-reducer";
import type { VpsReducerAction } from "../lib/src/phrase-building/vps-reducer";
import { makeVPSelectionState } from "../lib/src/phrase-building/verb-selection";
import APPicker from "./src/block-pickers/APPicker";
import VPDisplay from "./src/vp-explorer/VPDisplay";
import VPPicker from "./src/vp-explorer/VPPicker";
import NPDisplay from "./src/vp-explorer/NPDisplay";
import HumanReadableInflectionPattern from "./src/HumanReadableInflectionPattern";
import { psJSXMap } from "./src/jsx-map";
import HumanReadableInflectionPattern from "./src/tables/HumanReadableInflectionPattern";
import { psJSXMap } from "./src/text-display/jsx-map";
import genderColors from "./src/gender-colors";
// this library also includes everything from the core inflect library
export * from "../lib/library";
export {
useStickyState,
roleIcon,
vpsReducer,
makeVPSelectionState,
EPExplorer,
VPExplorer,
Examples,
VerbFormDisplay,
VerbTable,
VerbInfo,
RootsAndStems,
InflectionsTable,
Pashto,
Phonetics,
InlinePs,
ButtonSelect,
Hider,
EntrySelect,
NPPicker,
APPicker,
NPBlock,
APBlock,
Block,
EPDisplay,
VPDisplay,
NPDisplay,
EPPicker,
VPPicker,
CompiledPTextDisplay,
RenderedBlocksDisplay,
HumanReadableInflectionPattern,
psJSXMap,
genderColors,
}
// text-display
InlinePs,
Pashto,
Phonetics,
CompiledPTextDisplay,
Examples,
export type VpsReducerAction = VpsA;
// selects
ButtonSelect,
EntrySelect,
PersonSelection,
// tables
InflectionsTable,
VerbTable,
// block-pickers
APPicker,
NPPicker,
SandwichPicker,
// blocks
Block,
// misc
Hider,
useStickyState,
VerbFromDisplay,
VerbInfo,
VPExplorer,
EPExplorer,
playAudio,
roleIcon,
vpsReducer,
VpsReducerAction,
makeVPSelectionState,
RootsAndStems,
VPDisplay,
VPPicker,
NPDisplay,
HumanReadableInflectionPattern,
psJSXMap,
genderColors,
};

File diff suppressed because it is too large Load Diff

View File

@ -18,35 +18,24 @@
"LICENSE"
],
"scripts": {
"build": "echo \"build from repo root\"",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
"build": "echo \"build from repo root\""
},
"author": "lingdocs.com",
"license": "MIT",
"license": "GPL-3.0",
"dependencies": {
"@formkit/auto-animate": "^1.0.0-beta.3",
"classnames": "^2.2.6",
"classnames": "^2.5.1",
"fp-ts": "^2.16.0",
"jsurl2": "^2.1.0",
"lokijs": "^1.5.12",
"lz-string": "^1.4.4",
"pbf": "^3.2.1",
"micro-memoize": "^4.1.2",
"rambda": "^7.3.0",
"react-bootstrap": "^1.5.1",
"react-error-boundary": "^4.0.13",
"react-select": "^5.4.0"
},
"devDependencies": {
"@babel/core": "^7.21.0",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
"@storybook/addon-interactions": "^6.5.16",
"@storybook/addon-links": "^6.5.16",
"@storybook/builder-webpack4": "^6.5.16",
"@storybook/manager-webpack4": "^6.5.16",
"@storybook/react": "^6.5.16",
"@storybook/testing-library": "^0.0.13",
"@types/pbf": "^3.0.2",
"babel-loader": "^8.3.0",
"fs-extra": "^10.1.0"
"@types/lokijs": "^1.5.14",
"fs-extra": "^11.2.0"
}
}

View File

@ -1,48 +0,0 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import classNames from "classnames";
type PickerProps<T extends string> = {
options: { label: any, value: T, color?: string }[],
value: T,
handleChange: (payload: T) => void,
small?: boolean,
xSmall?: boolean,
faded?: boolean,
}
function ButtonSelect<L extends string>(props: PickerProps<L>) {
return <div className="btn-group">
{props.options.map((option) => (
<button
key={option.value}
type="button"
className={classNames(
"btn",
props.faded ? "btn-light" : "btn-outline-secondary",
{ active: props.value === option.value },
{ "btn-sm": props.small || props.xSmall },
)}
onClick={() => props.handleChange(option.value)}
style={{
...props.xSmall ?
{ fontSize: "small" }: {},
...(option.color && (props.value === option.value)) ?
{ backgroundColor: option.color } : {},
}}
>
<span className={classNames([{ "text-on-gender-color": option.color && (props.value === option.value) }])}>
{option.label}
</span>
</button>
))}
</div>
}
export default ButtonSelect;

View File

@ -1,37 +0,0 @@
import { getLength, getLong } from "../../lib/src/p-text-helpers";
import * as T from "../../types";
import Examples from "./Examples";
function CompiledPTextDisplay({ compiled, opts, justify, onlyOne, length }: {
compiled: {
ps: T.SingleOrLengthOpts<T.PsString[]>;
e?: string[] | undefined;
},
opts: T.TextOptions,
justify?: "left" | "right" | "center",
onlyOne?: boolean,
length?: "long" | "short",
}) {
function VariationLayer({ vs }: { vs: T.PsString[] }) {
return <div className="mb-2">
<Examples opts={opts} lineHeight={0}>{vs}</Examples>
</div>;
}
const ps = length
? getLength(compiled.ps, length)
: compiled.ps;
return <div className={justify === "left" ? "text-left" : justify === "right" ? "text-right" : "text-center"}>
{onlyOne
? <VariationLayer vs={[getLong(ps)[0]]} />
: "long" in ps ?
<div>
<VariationLayer vs={ps.long} />
<VariationLayer vs={ps.short} />
{ps.mini && <VariationLayer vs={ps.mini} />}
</div>
: <VariationLayer vs={ps} />
}
</div>;
}
export default CompiledPTextDisplay;

View File

@ -9,65 +9,88 @@
import { createElement, useEffect, useRef } from "react";
import classNames from "classnames";
import * as T from "../../types";
// @ts-expect-error types needed
import autoAnimate from "@formkit/auto-animate";
const caretRight = <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-caret-right-fill" viewBox="0 0 16 16">
<path d="M12.14 8.753l-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
</svg>
const caretDown = <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-caret-down-fill" viewBox="0 0 16 16">
<path d="M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
</svg>
const caretRight = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-caret-right-fill"
viewBox="0 0 16 16"
>
<path d="M12.14 8.753l-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z" />
</svg>
);
const caretDown = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-caret-down-fill"
viewBox="0 0 16 16"
>
<path d="M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z" />
</svg>
);
const defaultLevel = 4;
const indentAfterLevel = 5;
function Hider(props: {
label: string | JSX.Element,
showing: boolean,
aspect?: T.Aspect,
handleChange: () => void,
children: React.ReactNode,
hLevel?: number,
ignore?: boolean,
label: string | JSX.Element;
showing: boolean;
aspect?: T.Aspect;
handleChange: () => void;
children: React.ReactNode;
hLevel?: number;
ignore?: boolean;
}) {
const parent = useRef<HTMLDivElement>(null);
useEffect(() => {
parent.current && autoAnimate(parent.current);
}, [parent]);
const hLev = Math.min((props.hLevel ? props.hLevel : defaultLevel), 6);
const extraMargin = (props.hLevel && (props.hLevel > indentAfterLevel))
? `ml-${(props.hLevel - indentAfterLevel) + 1}`
: "";
if (props.ignore) {
return <>
{props.children}
</>;
const parent = useRef<HTMLDivElement>(null);
useEffect(() => {
if (parent.current) {
autoAnimate(parent.current);
}
return <div className="mb-3" ref={parent}>
{createElement(
`h${hLev}`,
{
onClick: props.handleChange,
className: classNames(
"clickable",
extraMargin,
),
},
<div className="d-flex flex-row align-items-center">
<div style={{ width: "1rem" }}>
{props.showing ? caretDown : caretRight}
</div>
{` `}
{props.aspect
? <i className={`fas fa-${props.aspect === "imperfective" ? "video" : "camera"}`} />
: ""}
<div className="ml-2">
{props.label}
</div>
</div>,
)}
{props.showing && props.children}
}, [parent]);
const hLev = Math.min(props.hLevel ? props.hLevel : defaultLevel, 6);
const extraMargin =
props.hLevel && props.hLevel > indentAfterLevel
? `ml-${props.hLevel - indentAfterLevel + 1}`
: "";
if (props.ignore) {
return <>{props.children}</>;
}
return (
<div className="mb-3" ref={parent}>
{createElement(
`h${hLev}`,
{
onClick: props.handleChange,
className: classNames("clickable", extraMargin),
},
<div className="d-flex flex-row align-items-center">
<div style={{ width: "1rem" }}>
{props.showing ? caretDown : caretRight}
</div>
{` `}
{props.aspect ? (
<i
className={`fas fa-${
props.aspect === "imperfective" ? "video" : "camera"
}`}
/>
) : (
""
)}
<div className="ml-2">{props.label}</div>
</div>
)}
{props.showing && props.children}
</div>
);
}
export default Hider;
export default Hider;

View File

@ -1,37 +0,0 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import Pashto from "./Pashto";
import Phonetics from "./Phonetics";
import * as T from "../../types";
function InlinePs ({
children,
ps,
opts,
}: ({
ps: T.PsString | (T.PsJSX & { e?: string }),
children?: T.PsString | (T.PsJSX & { e?: string }),
opts: T.TextOptions,
} | {
ps?: T.PsString | (T.PsJSX & { e?: string }),
children: T.PsString | (T.PsJSX & { e?: string }),
opts: T.TextOptions,
})) {
const text = children || ps as T.PsString | (T.PsJSX & { e?: string });
return (
<span>
<Pashto opts={opts}>{text}</Pashto>
{opts.phonetics !== "none" && " - "}
<Phonetics opts={opts}>{text}</Phonetics>
{text.e && <span className="text-muted"> ({text.e})</span>}
</span>
);
}
export default InlinePs;

View File

@ -1,108 +0,0 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import { persons } from "../../lib/src/grammar-units";
import InlinePs from "./InlinePs";
import * as T from "../../types";
function PersonSelect(props: {
setting: "subject" | "object",
value: T.Person,
locked?: boolean,
handleChange: (person: T.Person) => void,
handleRandom: () => void,
}) {
return (
!props.locked ? <div className="input-group" style={{ maxWidth: "30rem" }}>
<select
className="custom-select"
value={props.value}
onChange={(e: any) => props.handleChange(
parseInt(e.target.value) as T.Person
)}
>
{persons.map((p) => (
<option value={p.person} key={"subject"+p.person}>
{p.label[props.setting]}
</option>
))}
</select>
<div className="input-group-append" onClick={props.handleRandom}>
<button className="btn btn-secondary">
<i className="fas fa-random" />
</button>
</div>
</div> : <input
className="form-control"
type="text"
placeholder={persons[props.value].label[props.setting]}
readOnly
/>
);
}
function PersonSelection(props: {
subject: T.Person,
object: T.Person,
info: T.NonComboVerbInfo,
handleRandom: (setting: "subject" | "object") => void,
handleChange: (payload: { setting: "subject" | "object", person: T.Person }) => void,
textOptions: T.TextOptions,
}) {
function getComp(comp: T.ObjComplement) {
const c = comp.plural
? comp.plural
: comp.entry;
return <InlinePs opts={props.textOptions}>{c}</InlinePs>;
}
return (
<div className="row align-items-baseline">
<div className="col">
<label className="form-label">
{/* TODO: Should I put the Subject/Agent label back in for non-transitive verbs?? */}
<strong>Subject</strong>
</label>
<PersonSelect
setting="subject"
value={props.subject}
handleChange={(person: T.Person) => props.handleChange({ setting: "subject", person })}
handleRandom={() => props.handleRandom("subject")}
/>
</div>
{(props.info.type === "dynamic compound" || props.info.type === "generative stative compound") ? <div className="col">
<label className="form-label"><strong>Object is the complement ({getComp(props.info.objComplement)})</strong></label>
<PersonSelect
setting="object"
value={props.info.objComplement.person}
locked
handleChange={(person: T.Person) => props.handleChange({ setting: "object", person })}
handleRandom={() => props.handleRandom("object")}
/>
</div> : props.info.transitivity === "transitive" ? <div className="col">
<label className="form-label"><strong>Object</strong></label>
<PersonSelect
setting="object"
value={props.object}
handleChange={(person: T.Person) => props.handleChange({ setting: "object", person })}
handleRandom={() => props.handleRandom("object")}
/>
</div> : props.info.transitivity === "grammatically transitive" ? <div className="col">
<label className="form-label"><strong>Object is unspoken</strong></label>
<PersonSelect
setting="object"
value={10}
locked
handleChange={(person: T.Person) => props.handleChange({ setting: "object", person })}
handleRandom={() => props.handleRandom("object")}
/>
</div> : null}
</div>
);
}
export default PersonSelection;

View File

@ -1,38 +0,0 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import {
translatePhonetics,
} from "../../lib/src/translate-phonetics";
import { psJSXMap } from "./jsx-map";
import * as T from "../../types";
const Phonetics = ({ opts, children: text }: {
opts: T.TextOptions,
children: T.PsJSX | T.PsString | string,
}) => {
if (opts.phonetics === "none") {
return null;
}
const handleText = (f: string) => (
opts.phonetics === "lingdocs"
? f
: translatePhonetics(f, {
dialect: opts.dialect,
// @ts-ignore - weird TS not picking up the elimination of "none herre"
system: opts.phonetics,
})
);
return <span className="f-text">
{(typeof text !== "string" && typeof text.f !== "string")
? psJSXMap(text as T.PsJSX, "f", ({f}) => handleText(f))
: handleText(typeof text === "string" ? text : text.f as string)}
</span>
};
export default Phonetics;

View File

@ -1,67 +0,0 @@
import { useState } from "react";
import { filterForVisibleBlocksEP, filterForVisibleBlocksVP } from "../../lib/src/phrase-building/compile";
import * as T from "../../types";
import Block from "./blocks/Block";
import KidDisplay from "./blocks/KidDisplay";
function RenderedBlocksDisplay({ opts, rendered, justify, script }: {
script: "p" | "f",
opts: T.TextOptions,
rendered: T.EPRendered | T.VPRendered,
justify?: "left" | "right" | "center",
}) {
const [variation, setVariation] = useState<number>(0);
// not using autoAnimate here because we need a way to persist the keys in the blocks first
// const parent = useRef(null);
// useEffect(() => {
// parent.current && autoAnimate(parent.current)
// }, [parent]);
const blocksWVars: T.Block[][] = Array.isArray(rendered)
? rendered
: ("omitSubject" in rendered)
? filterForVisibleBlocksEP(rendered.blocks, rendered.omitSubject)
: filterForVisibleBlocksVP(rendered.blocks, rendered.form, rendered.king);
const king = "king" in rendered ? rendered.king : undefined;
const blocks = blocksWVars[variation];
function handleVariationChange() {
setVariation(ov => ((ov + 1) % blocksWVars.length));
}
return <div className={`d-flex flex-row justify-content-${justify ? justify : "center"}`}>
<div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} justify-content-left align-items-end mt-3 pb-2`} style={{ overflowX: "auto" }}>
<div key={blocks[0].key} className="mr-2">
<Block opts={opts} block={blocks[0]} king={king} script={script} />
</div>
<KidsSection key="kidsSection" opts={opts} kids={rendered.kids} script={script} />
{blocks.slice(1).map((block) => (
<div key={block.key} className="mr-2">
<Block opts={opts} block={block} king={king} script={script} />
</div>
))}
<div style={{ height: "100%" }} className="d-flex flex-column justify-content-center">
{blocksWVars.length > 1 && <button onClick={handleVariationChange} className="btn btn-light btn-sm mx-2">V. {variation + 1}/{blocksWVars.length}</button>}
</div>
</div>
</div>
}
function KidsSection({ opts, kids, script }: {
opts: T.TextOptions,
kids: T.Kid[],
script: "p" | "f",
}) {
// not using autoAnimate here because we need a way to persist the keys in the blocks first
// const parent = useRef(null);
// useEffect(() => {
// parent.current && autoAnimate(parent.current)
// }, [parent]);
return kids.length > 0 ? <div className="text-center mx-1 mr-3" style={{ paddingBottom: "1rem"}}>
<div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} mb-3 justify-content-center`}>
{kids.map(kid => (
<KidDisplay key={kid.key} opts={opts} kid={kid} script={script} />
))}
</div>
<div><strong>kids</strong></div>
</div> : null;
}
export default RenderedBlocksDisplay;

View File

@ -1,26 +0,0 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import Pashto from "./Pashto";
import Phonetics from "./Phonetics";
import * as T from "../../types";
function SingleItemDisplay({ item, textOptions, english }: {
item: T.PsString,
textOptions: T.TextOptions,
english?: T.EnglishBlock | string,
}) {
const eng = Array.isArray(english) ? english[0][0] : english;
return <div className="text-center mt-3 mb-2">
<div><Pashto opts={textOptions}>{item}</Pashto></div>
<div><Phonetics opts={textOptions}>{item}</Phonetics></div>
{eng && <div className="text-muted">{eng}</div>}
</div>;
}
export default SingleItemDisplay;

View File

@ -7,11 +7,11 @@
*/
import { useEffect, useState } from "react";
import PersonInfsPicker from "./PersInfsPicker";
import InflectionsTable from "./InflectionsTable";
import SingleItemDisplay from "./SingleItemDisplay";
import ButtonSelect from "./ButtonSelect";
import VerbTable from "./VerbTable";
import PersonInfsPicker from "./selects/PersInfsPicker";
import InflectionsTable from "./tables/InflectionsTable";
import SingleItemDisplay from "./text-display/SingleItemDisplay";
import ButtonSelect from "./selects/ButtonSelect";
import VerbTable from "./tables/VerbTable";
import {
getEnglishPersonInfo,
isSentenceForm,

View File

@ -1,111 +0,0 @@
/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the GPL3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import TableCell from "./TableCell";
import {
psStringEquals,
isAllOne,
addEnglish,
} from "../../lib/src/p-text-helpers";
import { isSentenceForm } from "../../lib/src/misc-helpers";
import * as T from "../../types";
import genderColors from "./gender-colors";
const genderAbbrev = (gender: "masc" | "fem" | undefined): " m." | " f." | "" => (
gender === "masc"
? " m."
: gender === "fem"
? " f."
: ""
);
const minifyTableGender = (block: T.VerbBlock | T.ImperativeBlock): Array<T.PersonLine | {
masc: T.PersonLine,
fem: T.PersonLine,
}> => {
// @ts-ignore
return block.reduce((table, person, i, src) => {
const isFem = i % 2 !== 0;
if (isFem) {
return table;
}
const femPersAhead = src[i+1];
const femPersIsTheSame = (
psStringEquals(person[0][0], femPersAhead[0][0]) &&
psStringEquals(person[1][0], femPersAhead[1][0])
);
if (femPersAhead && !femPersIsTheSame) {
return [...table, {
masc: person,
fem: femPersAhead,
}];
}
return [...table, person];
}, []);
};
function VerbTable({ block, textOptions, english }: {
block: T.VerbBlock | T.ImperativeBlock | T.ArrayOneOrMore<T.PsString>,
english?: T.EnglishBlock | string,
textOptions: T.TextOptions,
}) {
const blockWEng = english ? addEnglish(english, block) : block;
if (isSentenceForm(blockWEng) || isAllOne(blockWEng as T.VerbBlock | T.ImperativeBlock)) {
const item = isSentenceForm(blockWEng)
? block as unknown as T.ArrayOneOrMore<T.PsString>
: (() => {
const b = block as T.ImperativeBlock | T.VerbBlock
return b[0][0];
})();
return <table className="table text-center">
<tbody>
<tr>
<TableCell item={item} textOptions={textOptions} center noBorder />
</tr>
</tbody>
</table>
}
const bl = blockWEng as T.VerbBlock | T.ImperativeBlock;
const b = minifyTableGender(bl);
return <table className="table mt-2" style={{ tableLayout: "fixed" }}>
<thead>
<tr>
<th scope="col" style={{ width: "3rem" }}>Pers.</th>
<th scope="col">Singular</th>
<th scope="col">Plural</th>
</tr>
</thead>
<tbody>
{b.reduce((rows: React.ReactNode[], person, i, arr) => {
function drawRow({ line, gender }: { line: T.PersonLine, gender?: "masc" | "fem" }) {
const pers = arr.length > 1 ? ["1st", "2nd", "3rd"] : ["2nd"];
const rowLabel = `${pers[i]}${genderAbbrev(gender)}`;
const color = !gender
? "inherit"
: genderColors[gender];
return (
<tr key={`${i}${gender}`}>
<th scope="row" style={{ color }}>{rowLabel}</th>
<TableCell item={line[0]} textOptions={textOptions} />
<TableCell item={line[1]} textOptions={textOptions} />
</tr>
);
}
return "masc" in person
? [
...rows,
drawRow({ line: person.masc, gender: "masc" }),
drawRow({ line: person.fem, gender: "fem" }),
]
: [...rows, drawRow({ line: person })];
}, [])}
</tbody>
</table>
}
export default VerbTable;

View File

@ -1,31 +0,0 @@
import { makeAdverbSelection } from "../../../lib/src/phrase-building/make-selections";
import * as T from "../../../types";
import EntrySelect from "../EntrySelect";
function AdverbPicker(props: {
entryFeeder: T.EntryFeederSingleType<T.AdverbEntry>,
adjective: T.AdverbSelection | undefined,
onChange: (p: T.AdverbSelection | undefined) => void,
opts: T.TextOptions,
}) {