Merge branch 'develop' into Documentation-auto-generation

* develop: (22 commits)
  components-lint respects components prettierignore
  format main.css and prettierignore base.css
  scoped tailwind preflight, top-level .squiggle wrapper
  grammar: identifiers with modules
  Refactored reducerInterface files
  Added simple scientific units
  Added stdev to table of stats, when needed
  Added stdev and variance to Distribution Operation Constructors
  Quick addition of stdev-variance-min-max-mode
  ⬆️ Bump @types/node from 17.0.38 to 17.0.40
  ⬆️ Bump webpack from 5.72.1 to 5.73.0
  ⬆️ Bump @storybook/react from 6.5.6 to 6.5.7
  ⬆️ Bump ts-node from 10.8.0 to 10.8.1
  ⬆️ Bump @storybook/builder-webpack5 from 6.5.6 to 6.5.7
  ⬆️ Bump peggy from 2.0.0 to 2.0.1
  ⬆️ Bump @storybook/addon-essentials from 6.5.6 to 6.5.7
  ⬆️ Bump @storybook/preset-create-react-app from 4.1.1 to 4.1.2
  ⬆️ Bump @hookform/resolvers from 2.8.10 to 2.9.0
  ⬆️ Bump typescript from 4.7.2 to 4.7.3
  ⬆️ Bump @storybook/manager-webpack5 from 6.5.6 to 6.5.7
  ...
This commit is contained in:
Ozzie Gooen 2022-06-09 16:50:46 -07:00
commit 8fd5c6a1be
34 changed files with 1352 additions and 796 deletions

View File

@ -100,7 +100,7 @@ jobs:
uses: creyD/prettier_action@v4.2
with:
dry: true
prettier_options: --check packages/components
prettier_options: --check packages/components --ignore-path packages/components/.prettierignore
components-bundle-build:
name: Components bundle and build

View File

@ -1,2 +1,3 @@
dist/
storybook-static
src/styles/base.css

View File

@ -1,4 +1,15 @@
import "../src/tailwind.css";
import "../src/styles/main.css";
import "!style-loader!css-loader!postcss-loader!../src/styles/main.css";
import { SquiggleContainer } from "../src/components/SquiggleContainer";
export const decorators = [
(Story) => (
<SquiggleContainer>
<Story />
</SquiggleContainer>
),
];
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {

View File

@ -5,7 +5,7 @@
"dependencies": {
"@headlessui/react": "^1.6.4",
"@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^2.8.10",
"@hookform/resolvers": "^2.9.0",
"@quri/squiggle-lang": "^0.2.8",
"@react-hook/size": "^2.1.2",
"lodash": "^4.17.21",
@ -22,42 +22,44 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.17.12",
"@storybook/addon-actions": "^6.5.6",
"@storybook/addon-essentials": "^6.5.6",
"@storybook/addon-links": "^6.5.6",
"@storybook/builder-webpack5": "^6.5.6",
"@storybook/manager-webpack5": "^6.5.6",
"@storybook/addon-actions": "^6.5.7",
"@storybook/addon-essentials": "^6.5.7",
"@storybook/addon-links": "^6.5.7",
"@storybook/builder-webpack5": "^6.5.7",
"@storybook/manager-webpack5": "^6.5.7",
"@storybook/node-logger": "^6.5.6",
"@storybook/preset-create-react-app": "^4.1.1",
"@storybook/react": "^6.5.6",
"@storybook/preset-create-react-app": "^4.1.2",
"@storybook/react": "^6.5.7",
"@tailwindcss/forms": "^0.5.2",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.2.0",
"@types/jest": "^27.5.0",
"@types/lodash": "^4.14.182",
"@types/node": "^17.0.36",
"@types/node": "^17.0.40",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.5",
"@types/styled-components": "^5.1.24",
"@types/webpack": "^5.28.0",
"cross-env": "^7.0.3",
"mini-css-extract-plugin": "^2.6.0",
"postcss-cli": "^9.1.0",
"postcss-import": "^14.1.0",
"postcss-loader": "^7.0.0",
"react-scripts": "^5.0.1",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.24",
"ts-loader": "^9.3.0",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.7.2",
"typescript": "^4.7.3",
"web-vitals": "^2.1.4",
"webpack": "^5.72.1",
"webpack": "^5.73.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
},
"scripts": {
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
"build": "tsc -b && tailwindcss -i ./src/tailwind.css -o ./dist/main.css && build-storybook -s public",
"build": "tsc -b && postcss ./src/styles/main.css -o ./dist/main.css && build-storybook -s public",
"bundle": "webpack",
"all": "yarn bundle && yarn build",
"lint": "prettier --check .",

View File

@ -1,5 +1,7 @@
module.exports = {
plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {},
autoprefixer: {},
cssnano: {},

View File

@ -4,6 +4,7 @@ import {
InformationCircleIcon,
CheckCircleIcon,
} from "@heroicons/react/solid";
import clsx from "clsx";
export const Alert: React.FC<{
heading: string;
@ -23,18 +24,18 @@ export const Alert: React.FC<{
children,
}) => {
return (
<div className={`rounded-md p-4 ${backgroundColor}`}>
<div className={clsx("rounded-md p-4", backgroundColor)}>
<div className="flex">
<Icon
className={`h-5 w-5 flex-shrink-0 ${iconColor}`}
className={clsx("h-5 w-5 flex-shrink-0", iconColor)}
aria-hidden="true"
/>
<div className="ml-3">
<header className={`text-sm font-medium ${headingColor}`}>
<header className={clsx("text-sm font-medium", headingColor)}>
{heading}
</header>
{children && React.Children.count(children) ? (
<div className={`mt-2 text-sm ${bodyColor}`}>{children}</div>
<div className={clsx("mt-2 text-sm", bodyColor)}>{children}</div>
) : null}
</div>
</div>

View File

@ -9,6 +9,8 @@ import { Vega, VisualizationSpec } from "react-vega";
import * as chartSpecification from "../vega-specs/spec-distributions.json";
import { ErrorAlert } from "./Alert";
import { useSize } from "react-use";
import clsx from "clsx";
import {
linearXScale,
logXScale,
@ -128,7 +130,7 @@ export const CheckBox: React.FC<CheckBoxProps> = ({
onChange={() => onChange(!value)}
disabled={disabled}
/>
<label className={disabled ? "text-slate-400" : ""}> {label}</label>
<label className={clsx(disabled && "text-slate-400")}> {label}</label>
</span>
);
};
@ -153,6 +155,7 @@ type SummaryTableProps = {
const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
const mean = distribution.mean();
const stdev = distribution.stdev();
const p5 = distribution.inv(0.05);
const p10 = distribution.inv(0.1);
const p25 = distribution.inv(0.25);
@ -161,6 +164,9 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
const p90 = distribution.inv(0.9);
const p95 = distribution.inv(0.95);
const hasResult = (x: result<number, distributionError>): boolean =>
x.tag === "Ok";
const unwrapResult = (
x: result<number, distributionError>
): React.ReactNode => {
@ -180,6 +186,7 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
<thead className="bg-slate-50">
<tr>
<TableHeadCell>{"Mean"}</TableHeadCell>
{hasResult(stdev) && <TableHeadCell>{"Stdev"}</TableHeadCell>}
<TableHeadCell>{"5%"}</TableHeadCell>
<TableHeadCell>{"10%"}</TableHeadCell>
<TableHeadCell>{"25%"}</TableHeadCell>
@ -192,6 +199,7 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
<tbody>
<tr>
<Cell>{unwrapResult(mean)}</Cell>
{hasResult(stdev) && <Cell>{unwrapResult(stdev)}</Cell>}
<Cell>{unwrapResult(p5)}</Cell>
<Cell>{unwrapResult(p10)}</Cell>
<Cell>{unwrapResult(p25)}</Cell>

View File

@ -0,0 +1,25 @@
import React, { useContext } from "react";
type Props = {
children: React.ReactNode;
};
type SquiggleContextShape = {
containerized: boolean;
};
const SquiggleContext = React.createContext<SquiggleContextShape>({
containerized: false,
});
export const SquiggleContainer: React.FC<Props> = ({ children }) => {
const context = useContext(SquiggleContext);
if (context.containerized) {
return <>{children}</>;
} else {
return (
<SquiggleContext.Provider value={{ containerized: true }}>
<div className="squiggle">{children}</div>
</SquiggleContext.Provider>
);
}
};

View File

@ -15,6 +15,7 @@ import {
defaultBindings,
} from "@quri/squiggle-lang";
import { ErrorAlert } from "./Alert";
import { SquiggleContainer } from "./SquiggleContainer";
export interface SquiggleEditorProps {
/** The input string for squiggle */
@ -64,6 +65,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
count: diagramCount,
};
return (
<SquiggleContainer>
<div>
<div className="border border-grey-200 p-2 m-4">
<CodeEditor
@ -87,6 +89,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
showSummary={showSummary}
/>
</div>
</SquiggleContainer>
);
};
@ -171,6 +174,7 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({
React.useEffect(runSquiggleAndUpdateBindings, [expression]);
return (
<SquiggleContainer>
<div>
<div className="border border-grey-200 p-2 m-4">
<CodeEditor
@ -181,8 +185,11 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({
height={20}
/>
</div>
{error !== null ? <ErrorAlert heading="Error">{error}</ErrorAlert> : null}
{error !== null ? (
<ErrorAlert heading="Error">{error}</ErrorAlert>
) : null}
</div>
</SquiggleContainer>
);
};

View File

@ -10,6 +10,7 @@ import {
CogIcon,
CurrencyDollarIcon,
} from "@heroicons/react/solid";
import clsx from "clsx";
import { defaultBindings, environment } from "@quri/squiggle-lang";
@ -17,6 +18,7 @@ import { SquiggleChart } from "./SquiggleChart";
import { CodeEditor } from "./CodeEditor";
import { JsonEditor } from "./JsonEditor";
import { ErrorAlert, SuccessAlert } from "./Alert";
import { SquiggleContainer } from "./SquiggleContainer";
interface PlaygroundProps {
/** The initial squiggle string to put in the playground */
@ -87,10 +89,6 @@ const schema = yup
})
.required();
function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ");
}
type StyledTabProps = {
name: string;
icon: (props: React.ComponentProps<"svg">) => JSX.Element;
@ -102,15 +100,13 @@ const StyledTab: React.FC<StyledTabProps> = ({ name, icon: Icon }) => {
{({ selected }) => (
<button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100">
<span
className={classNames(
className={clsx(
"p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium",
selected
? "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
: ""
selected && "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
)}
>
<Icon
className={classNames(
className={clsx(
"-ml-0.5 mr-2 h-4 w-4",
selected
? "text-slate-500"
@ -118,11 +114,11 @@ const StyledTab: React.FC<StyledTabProps> = ({ name, icon: Icon }) => {
)}
/>
<span
className={
className={clsx(
selected
? "text-gray-900"
: "text-gray-600 group-hover:text-gray-900"
}
)}
>
{name}
</span>
@ -160,13 +156,14 @@ function InputItem<T>({
type: "number";
register: UseFormRegister<T>;
}) {
const numberStyle =
"max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md";
return (
<label className="block">
<div className="text-sm font-medium text-gray-600 mb-1">{label}</div>
<input type={type} {...register(name)} className={numberStyle} />
<input
type={type}
{...register(name)}
className="max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md"
/>
</label>
);
}
@ -378,6 +375,7 @@ const SquigglePlayground: FC<PlaygroundProps> = ({
);
return (
<SquiggleContainer>
<Tab.Group>
<div className="pb-4">
<Tab.List className="flex w-fit p-0.5 rounded-md bg-slate-100 hover:bg-slate-200">
@ -424,8 +422,10 @@ const SquigglePlayground: FC<PlaygroundProps> = ({
</div>
</div>
</Tab.Group>
</SquiggleContainer>
);
};
export default SquigglePlayground;
export function renderSquigglePlaygroundToDom(props: PlaygroundProps) {
const parent = document.createElement("div");

View File

@ -9,5 +9,6 @@ export {
default as SquigglePlayground,
renderSquigglePlaygroundToDom,
} from "./components/SquigglePlayground";
export { SquiggleContainer } from "./components/SquiggleContainer";
export { mergeBindings } from "@quri/squiggle-lang";

View File

@ -0,0 +1,510 @@
.squiggle {
/*
This file contains:
1) Base Tailwind preflight styles
2) Base https://github.com/tailwindlabs/tailwindcss-forms styles
(Both are wrapped in .squiggle)
*/
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
*/
/* html { */
line-height: 1.5; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-moz-tab-size: 4; /* 3 */
tab-size: 4; /* 3 */
font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 4 */
/* } */
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
/* body { */
margin: 0; /* 1 */
line-height: inherit; /* 2 */
/* } */
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: theme('borderColor.DEFAULT', currentColor); /* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
border-top-width: 1px; /* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); /* 1 */
font-size: 1em; /* 2 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-collapse: collapse; /* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button; /* 1 */
background-color: transparent; /* 2 */
background-image: none; /* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::placeholder,
textarea::placeholder {
opacity: 1; /* 1 */
color: theme('colors.gray.400', #9ca3af); /* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block; /* 1 */
vertical-align: middle; /* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/* these styles were generated by tailwindcss-forms */
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0px;
padding-top: 0.5rem;
padding-right: 0.75rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
font-size: 1rem;
line-height: 1.5rem;
--tw-shadow: 0 0 #0000;
}
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
border-color: #2563eb;
}
input::placeholder,textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
padding-top: 0;
padding-bottom: 0;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: 0.75rem;
-webkit-print-color-adjust: unset;
print-color-adjust: unset;
}
[type='checkbox'],[type='radio'] {
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
--tw-shadow: 0 0 #0000;
}
[type='checkbox'] {
border-radius: 0px;
}
[type='radio'] {
border-radius: 100%;
}
[type='checkbox']:focus,[type='radio']:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
[type='checkbox']:checked,[type='radio']:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type='radio']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
border-color: transparent;
background-color: currentColor;
}
[type='checkbox']:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
border-color: transparent;
background-color: currentColor;
}
[type='file'] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type='file']:focus {
outline: 1px solid ButtonText;
outline: 1px auto -webkit-focus-ring-color;
}
}

View File

@ -0,0 +1,8 @@
@import "./base.css";
@tailwind components;
@tailwind utilities;
/* necessary hack because scoped preflight in ./base.css has higher specificity */
.ace_cursor {
border-left: 2px solid !important;
}

View File

@ -1,4 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind forms;

View File

@ -3,5 +3,6 @@ module.exports = {
theme: {
extend: {},
},
important: ".squiggle",
plugins: [require("@tailwindcss/forms")],
};

View File

@ -5,7 +5,7 @@ module.exports = {
mode: "production",
devtool: "source-map",
profile: true,
entry: ["./src/index.ts", "./src/tailwind.css"],
entry: ["./src/index.ts", "./src/styles/main.css"],
module: {
rules: [
{

View File

@ -232,6 +232,7 @@ describe("Peggy parse", () => {
})
describe("unit", () => {
testParse("1m", "{(::fromUnit_m 1)}")
testParse("1M", "{(::fromUnit_M 1)}")
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
})
})

View File

@ -56,14 +56,14 @@
"lodash": "^4.17.21",
"moduleserve": "^0.9.1",
"nyc": "^15.1.0",
"peggy": "^2.0.0",
"peggy": "^2.0.1",
"reanalyze": "^2.22.0",
"rescript-fast-check": "^1.1.1",
"ts-jest": "^27.1.4",
"ts-loader": "^9.3.0",
"ts-node": "^10.8.0",
"typescript": "^4.7.2",
"webpack": "^5.72.1",
"ts-node": "^10.8.1",
"typescript": "^4.7.3",
"webpack": "^5.73.0",
"webpack-cli": "^4.9.2"
},
"source": "./src/js/index.ts",

View File

@ -11,6 +11,7 @@ import {
import { result, resultMap, Ok } from "./types";
import {
Constructors_mean,
Constructors_stdev,
Constructors_sample,
Constructors_pdf,
Constructors_cdf,
@ -69,6 +70,10 @@ export class Distribution {
return Constructors_mean({ env: this.env }, this.t);
}
stdev(): result<number, distributionError> {
return Constructors_stdev({ env: this.env }, this.t);
}
sample(): result<number, distributionError> {
return Constructors_sample({ env: this.env }, this.t);
}

View File

@ -265,6 +265,8 @@ module Constructors = {
module C = DistributionTypes.Constructors.UsingDists
open OutputLocal
let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
let stdev = (~env, dist) => C.stdev(dist)->run(~env)->toFloatR
let variance = (~env, dist) => C.variance(dist)->run(~env)->toFloatR
let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR
let cdf = (~env, dist, f) => C.cdf(dist, f)->run(~env)->toFloatR
let inv = (~env, dist, f) => C.inv(dist, f)->run(~env)->toFloatR

View File

@ -49,6 +49,10 @@ module Constructors: {
@genType
let mean: (~env: env, genericDist) => result<float, error>
@genType
let stdev: (~env: env, genericDist) => result<float, error>
@genType
let variance: (~env: env, genericDist) => result<float, error>
@genType
let sample: (~env: env, genericDist) => result<float, error>
@genType
let cdf: (~env: env, genericDist, float) => result<float, error>

View File

@ -30,9 +30,9 @@ module Error = {
@genType
let toString = (err: error): string =>
switch err {
| NotYetImplemented => "Function Not Yet Implemented"
| NotYetImplemented => "Function not yet implemented"
| Unreachable => "Unreachable"
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid"
| DistributionVerticalShiftIsInvalid => "Distribution vertical shift is invalid"
| ArgumentError(s) => `Argument Error ${s}`
| LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}`
| SampleSetError(TooFewSamples) => "Too Few Samples"
@ -68,6 +68,11 @@ module DistributionOperation = {
| #Mean
| #Sample
| #IntegralSum
| #Mode
| #Stdev
| #Min
| #Max
| #Variance
]
type toScaleFn = [
@ -117,6 +122,11 @@ module DistributionOperation = {
| ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
| ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
| ToFloat(#Mean) => `mean`
| ToFloat(#Min) => `min`
| ToFloat(#Max) => `max`
| ToFloat(#Stdev) => `stdev`
| ToFloat(#Variance) => `variance`
| ToFloat(#Mode) => `mode`
| ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
| ToFloat(#Sample) => `sample`
| ToFloat(#IntegralSum) => `integralSum`
@ -151,6 +161,8 @@ module Constructors = {
module UsingDists = {
@genType
let mean = (dist): t => FromDist(ToFloat(#Mean), dist)
let stdev = (dist): t => FromDist(ToFloat(#Stdev), dist)
let variance = (dist): t => FromDist(ToFloat(#Variance), dist)
let sample = (dist): t => FromDist(ToFloat(#Sample), dist)
let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist)
let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist)

View File

@ -108,7 +108,7 @@ let toFloatOperation = (
) => {
switch distToFloatOperation {
| #IntegralSum => Ok(integralEndY(t))
| (#Pdf(_) | #Cdf(_) | #Inv(_) | #Mean | #Sample) as op => {
| (#Pdf(_) | #Cdf(_) | #Inv(_) | #Mean | #Sample | #Min | #Max) as op => {
let trySymbolicSolution = switch (t: t) {
| Symbolic(r) => SymbolicDist.T.operate(op, r)->E.R.toOption
| _ => None
@ -118,6 +118,8 @@ let toFloatOperation = (
| (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some
| (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some
| (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some
| (SampleSet(sampleSet), #Min) => SampleSetDist.min(sampleSet)->Some
| (SampleSet(sampleSet), #Max) => SampleSetDist.max(sampleSet)->Some
| _ => None
}
@ -130,6 +132,16 @@ let toFloatOperation = (
}
}
}
| (#Stdev | #Variance | #Mode) as op =>
switch t {
| SampleSet(s) =>
switch op {
| #Stdev => SampleSetDist.stdev(s)->Ok
| #Variance => SampleSetDist.variance(s)->Ok
| #Mode => SampleSetDist.mode(s)->Ok
}
| _ => Error(DistributionTypes.NotYetImplemented)
}
}
}

View File

@ -254,6 +254,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
| #Inv(f) => inv(f, s)
| #Sample => sample(s)
| #Mean => T.mean(s)
| #Min => T.minX(s)
| #Max => T.maxX(s)
}
let toSparkline = (t: t, bucketCount): result<string, PointSetTypes.sparklineError> =>

View File

@ -449,6 +449,8 @@ module T = {
| #Cdf(f) => Ok(cdf(f, s))
| #Pdf(f) => Ok(pdf(f, s))
| #Inv(f) => Ok(inv(f, s))
| #Min => Ok(min(s))
| #Max => Ok(max(s))
| #Sample => Ok(sample(s))
| #Mean => mean(s)
}

View File

@ -224,10 +224,9 @@ postOperator = indexedValue
indexedValue
= collectionElement
/ recordElement
/ atom
collectionElement
collectionElement
= head:atom &('['/'('/'.')
tail:(
_ '[' _nl arg:expression _nl ']' {return {fn: postOperatorToFunction['[]'], args: [arg]}}
@ -242,13 +241,6 @@ indexedValue
= head:expression tail:(_ ',' _nl @expression)*
{ return [head, ...tail]; }
recordElement
= head:dollarIdentifier &'.'
tail:(_ '.' _nl arg:$dollarIdentifier {return {fn: postOperatorToFunction['.'], args: [nodeString(arg)]}})*
{ return tail.reduce(function(result, element) {
return makeFunctionCall(element.fn, [result, ...element.args])
}, head)}
atom
= '(' _nl expression:expression _nl ')' {return expression}
/ basicValue
@ -259,19 +251,36 @@ basicLiteral
= string
/ number
/ boolean
/ dollarIdentifierWithModule
/ dollarIdentifier
dollarIdentifierWithModule 'identifier'
= head:moduleIdentifier
tail:('.' _nl @moduleIdentifier)* '.' _nl
final:dollarIdentifier
{ tail.push(final);
return tail.reduce(function(result, element) {
return makeFunctionCall(postOperatorToFunction['[]'], [result, element])
}, head)}
identifier 'identifier'
= ([_a-z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
unitIdentifier 'identifier'
= ([_a-zA-Z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
dollarIdentifier '$identifier'
= ([\$_a-z]+[\$_a-z0-9]i*) {return nodeIdentifier(text())}
moduleIdentifier 'identifier'
= ([A-Z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
string 'string'
= characters:("'" @([^'])* "'") {return nodeString(characters.join(''))}
/ characters:('"' @([^"])* '"') {return nodeString(characters.join(''))}
number = number:(float / integer) unit:identifier?
number = number:(float / integer) unit:unitIdentifier?
{
if (unit === null)
{ return number }
@ -335,7 +344,7 @@ recordConstructor 'record'
_ 'whitespace'
= whiteSpaceCharactersOrComment*
_nl 'optional whitespace or newline'
_nl 'whitespace or newline'
= (whiteSpaceCharactersOrComment / commentOrNewLine)*
__ 'whitespace'

View File

@ -0,0 +1,27 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [EvDate(t)]) => EV.EvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [EvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => EV.EvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [EvNumber(f)]) => EV.EvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [EvDate(f)]) => EV.EvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [EvDate(d1), EvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => EV.EvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}

View File

@ -1,32 +1,7 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
let dateDispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [EvDate(t)]) => EV.EvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [EvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => EV.EvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [EvNumber(f)]) => EV.EvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [EvDate(f)]) => EV.EvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [EvDate(d1), EvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => EV.EvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}
let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
@ -55,16 +30,3 @@ let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): op
| _ => None
}
}
let dispatch = (call: EV.functionCall, env: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch dateDispatch(call, env) {
| Some(r) => Some(r)
| None =>
switch durationDispatch(call, env) {
| Some(r) => Some(r)
| None => None
}
}
}

View File

@ -14,15 +14,27 @@ type expressionValue = ExpressionValue.expressionValue
Map external calls of Reducer
*/
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
let registry = FunctionRegistry_Library.registry
let tryRegistry = ((fnName, args): ExpressionValue.functionCall, env) => {
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
)
}
let dispatch = (call: ExpressionValue.functionCall, environment, chain): result<
expressionValue,
'e,
> =>
switch ReducerInterface_GenericDistribution.dispatch(call, environment) {
| Some(r) => r
| None =>
ReducerInterface_DateTime.dispatch(call, environment) |> E.O.default(chain(call, environment))
}
> => {
E.A.O.firstSomeFn([
() => ReducerInterface_GenericDistribution.dispatch(call, environment),
() => ReducerInterface_Date.dispatch(call, environment),
() => ReducerInterface_Duration.dispatch(call, environment),
() => ReducerInterface_Number.dispatch(call, environment),
() => tryRegistry(call, environment),
])->E.O2.default(chain(call, environment))
}
/*
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.

View File

@ -207,7 +207,18 @@ let dispatchToGenericOutput = (
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env)
| ("sampleN", [EvDistribution(dist), EvNumber(n)]) =>
Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n))))
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist, ~env)
| (("mean" | "stdev" | "variance" | "min" | "max" | "mode") as op, [EvDistribution(dist)]) => {
let fn = switch op {
| "mean" => #Mean
| "stdev" => #Stdev
| "variance" => #Variance
| "min" => #Min
| "max" => #Max
| "mode" => #Mode
| _ => #Mean
}
Helpers.toFloatFn(fn, dist, ~env)
}
| ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env)
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env)
| ("toSparkline", [EvDistribution(dist)]) =>
@ -348,20 +359,5 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
| GenDistError(err) => Error(REDistributionError(err))
}
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
let registry = FunctionRegistry_Library.registry
let tryRegistry = ((fnName, args): ExpressionValue.functionCall, env) => {
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
)
}
let dispatch = (call: ExpressionValue.functionCall, environment) => {
let regularDispatch =
let dispatch = (call: ExpressionValue.functionCall, environment) =>
dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)
switch regularDispatch {
| Some(x) => Some(x)
| None => tryRegistry(call, environment)
}
}

View File

@ -0,0 +1,45 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
module ScientificUnit = {
let nameToMultiplier = str =>
switch str {
| "n" => Some(1E-9)
| "m" => Some(1E-3)
| "k" => Some(1E3)
| "M" => Some(1E6)
| "B" => Some(1E9)
| "G" => Some(1E9)
| "T" => Some(1E12)
| "P" => Some(1E15)
| _ => None
}
let getMultiplier = (r: string) => {
let match = Js.String2.match_(r, %re(`/fromUnit_([_a-zA-Z]*)/`))
switch match {
| Some([_, unit]) => nameToMultiplier(unit)
| _ => None
}
}
}
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| (
("fromUnit_n"
| "fromUnit_m"
| "fromUnit_k"
| "fromUnit_M"
| "fromUnit_B"
| "fromUnit_G"
| "fromUnit_T"
| "fromUnit_P") as op,
[EvNumber(f)],
) =>
op->ScientificUnit.getMultiplier->E.O2.fmap(multiplier => EV.EvNumber(f *. multiplier)->Ok)
| _ => None
}
}

View File

@ -26,6 +26,8 @@ type distToFloatOperation = [
| #Inv(float)
| #Mean
| #Sample
| #Min
| #Max
]
module Convolution = {

View File

@ -16,7 +16,6 @@
"@docusaurus/preset-classic": "2.0.0-beta.21",
"@quri/squiggle-components": "^0.2.20",
"clsx": "^1.1.1",
"docusaurus-tailwindcss": "^0.1.0",
"hast-util-is-element": "2.1.2",
"prism-react-renderer": "^1.3.3",
"react": "^18.1.0",

1073
yarn.lock

File diff suppressed because it is too large Load Diff