1
0
Fork 0

feat: Wrapped up work on measurements sets

This commit is contained in:
joostdecock 2024-12-22 16:32:46 +01:00
parent 770b608090
commit dbe1a04552
23 changed files with 1292 additions and 359 deletions

View file

@ -1086,6 +1086,16 @@
- Migrated from Rollup to Esbuild for all builds - Migrated from Rollup to Esbuild for all builds
- The `pctBasedOn()` helper method for pattern config was moved to config-helpers We did not make this a breaking change since it's only used internally. - The `pctBasedOn()` helper method for pattern config was moved to config-helpers We did not make this a breaking change since it's only used internally.
### i18n
#### Changed
- Migrated from Rollup to Esbuild for all builds
#### Fixed
- Added missing lab namespace for English
### models ### models
#### Changed #### Changed
@ -1225,6 +1235,12 @@
## 2.20.2 (2022-01-27) ## 2.20.2 (2022-01-27)
### i18n
#### Fixed
- Patterns options were always in English due to symlinks being used
## 2.20.1 (2022-01-27) ## 2.20.1 (2022-01-27)
@ -1553,6 +1569,12 @@
- Added support for `settings.scale` - Added support for `settings.scale`
### i18n
#### Fixed
- Fixed issue that was causing plugin translations to always be in English
## 2.19.9 (2022-01-09) ## 2.19.9 (2022-01-09)
@ -1985,6 +2007,13 @@
- Pattern.on() is now chainable as it returns the Pattern object - Pattern.on() is now chainable as it returns the Pattern object
### i18n
#### Fixed
- Fixed bug in resolving of shared pattern options
- Removed optional chaining which broke node v12 support
### snapseries ### snapseries
#### Added #### Added
@ -2031,6 +2060,16 @@
- Handle path.offset() of very short curves with control points on the start or end point Closes [#1257](https://github.com/freesewing/freesewing/issues/1257) - Handle path.offset() of very short curves with control points on the start or end point Closes [#1257](https://github.com/freesewing/freesewing/issues/1257)
### i18n
#### Added
- Added translations for Yuri
#### Fixed
- Added optional chaining so missing options always lead to clear error message
## 2.17.4 (2021-08-20) ## 2.17.4 (2021-08-20)
@ -2043,6 +2082,12 @@
## 2.17.3 (2021-08-16) ## 2.17.3 (2021-08-16)
### i18n
#### Added
- New translations
### utils ### utils
#### Fixed #### Fixed
@ -2064,6 +2109,12 @@
- Added new ffsa option to let the user control the extra SA for flat-felled seams Closes [#1251](https://github.com/freesewing/freesewing/issues/1251) - Added new ffsa option to let the user control the extra SA for flat-felled seams Closes [#1251](https://github.com/freesewing/freesewing/issues/1251)
### i18n
#### Added
- Added new ffsa option for simon & simone
### models ### models
#### Added #### Added
@ -2185,9 +2236,21 @@
- Fix a bug in `path.shiftAlong` where no point is returned if the distance to shift is a fraction of one step (1/25mm) into a new path segment See [#1140](https://github.com/freesewing/freesewing/issues/1140) - Fix a bug in `path.shiftAlong` where no point is returned if the distance to shift is a fraction of one step (1/25mm) into a new path segment See [#1140](https://github.com/freesewing/freesewing/issues/1140)
### i18n
#### Changed
- Changed antman references to antperson
## 2.16.2 (2021-05-05) ## 2.16.2 (2021-05-05)
### i18n
#### Changed
- String updates
## 2.16.1 (2021-05-30) ## 2.16.1 (2021-05-30)
@ -2395,6 +2458,12 @@
- Changed `department` setting in config in line with new grouping - Changed `department` setting in config in line with new grouping
### i18n
#### Added
- New translations for pattern filter
### utils ### utils
#### Changed #### Changed
@ -2430,6 +2499,12 @@
- Fixed third button not showing up See [#973](https://github.com/freesewing/freesewing/issues/973) - Fixed third button not showing up See [#973](https://github.com/freesewing/freesewing/issues/973)
### i18n
#### Changed
- Changes to cfp strings
## 2.15.4 (2021-05-08) ## 2.15.4 (2021-05-08)
@ -2561,6 +2636,13 @@
- Don't round coordinates internally to avoid path.split misses - Don't round coordinates internally to avoid path.split misses
### i18n
#### Added
- Added translation for new Titan options
- Added translations for Charlie
## 2.14.0 (2021-03-07) ## 2.14.0 (2021-03-07)
@ -2576,6 +2658,12 @@
- Replaced grainline indicator on pocket with cut-on-fold indicator - Replaced grainline indicator on pocket with cut-on-fold indicator
### i18n
#### Added
- Added translations for Cornelius
## 2.13.2 (2021-02-21) ## 2.13.2 (2021-02-21)
@ -2683,6 +2771,12 @@
- Make sure roudEnd and roundStart points are always available - Make sure roudEnd and roundStart points are always available
### i18n
#### Added
- Translation for Hortensia
## 2.12.1 (2021-01-27) ## 2.12.1 (2021-01-27)
@ -2807,6 +2901,16 @@
- Removed unused lengthBonus option - Removed unused lengthBonus option
### i18n
#### Changed
- New strings for new features
#### Fixed
- Type in Simon title
## 2.10.7 (2020-11-18) ## 2.10.7 (2020-11-18)
@ -2822,6 +2926,12 @@
## 2.10.5 (2020-11-14) ## 2.10.5 (2020-11-14)
### i18n
#### Fixed
- Added missing `cty.` translations to non-English language files
## 2.10.4 (2020-11-13) ## 2.10.4 (2020-11-13)
@ -2884,6 +2994,18 @@
- Added the `info` type to raised events - Added the `info` type to raised events
- Added support for conditional loading of plugins - Added support for conditional loading of plugins
### i18n
#### Added
- Added translations for plugin-title
- Added translations for teagan
- Added some translations for the UI
#### Fixed
- Replaced a few identical files with symlinks
## 2.8.1 (2020-08-16) ## 2.8.1 (2020-08-16)
@ -3277,6 +3399,13 @@
- [Properly escape quotes in imperial units](https://github.com/freesewing/freesewing/issues/437) - [Properly escape quotes in imperial units](https://github.com/freesewing/freesewing/issues/437)
### i18n
#### Changed
- Added translations for Titan
- Removed `Circumference` suffix from measurement names
### models ### models
#### Changed #### Changed
@ -3344,6 +3473,12 @@
- utils now includes `Bezier` which holds the bezier-js library so you don't need to re-import it - utils now includes `Bezier` which holds the bezier-js library so you don't need to re-import it
- We no longer set the plugin configuration/data object to fall in `pattern.use()` - We no longer set the plugin configuration/data object to fall in `pattern.use()`
### i18n
#### Changed
- Changes to support the renaming of @freesewing/fu to @freesewing/florence
## 2.5.0 (2020-04-05) ## 2.5.0 (2020-04-05)
@ -3353,9 +3488,21 @@
- Diana is a top with a draped neck - Diana is a top with a draped neck
### i18n
#### Added
- title, description, and options for Dianna
## 2.4.6 (2020-03-23) ## 2.4.6 (2020-03-23)
### i18n
#### Fixed
- Fixed an bug in the i18n package
## 2.4.5 (2020-03-19) ## 2.4.5 (2020-03-19)
@ -3413,9 +3560,21 @@
## 2.4.3 (2020-03-12) ## 2.4.3 (2020-03-12)
### i18n
#### Added
- Added more translations
## 2.4.2 (2020-03-08) ## 2.4.2 (2020-03-08)
### i18n
#### Added
- Added more strings
## 2.4.1 (2020-03-04) ## 2.4.1 (2020-03-04)
@ -3588,6 +3747,22 @@
- Added the `Path.noop()` method - Added the `Path.noop()` method
- Added the `Path.insop()` methods - Added the `Path.insop()` methods
### i18n
#### Added
- Added translations for Breanna
#### Changed
- Added/Updated strings for the 2.2 frontend changes
- Changed `Joost De Cock` to `Joost` because spam filters don't like cock
#### Removed
- Removed the files for homepage translation, and moved that content to markdown
- Removed the files for editor translation, as it is no longer used
### models ### models
#### Changed #### Changed
@ -3652,6 +3827,12 @@
## 2.1.3 (2019-10-18) ## 2.1.3 (2019-10-18)
### i18n
#### Added
- More translated strings
### utils ### utils
#### Changed #### Changed
@ -3665,6 +3846,12 @@
## 2.1.2 (2019-10-14) ## 2.1.2 (2019-10-14)
### i18n
#### Fixed
- Fixed issue where symlinks were causing all languages to export English strings
## 2.1.1 (2019-10-13) ## 2.1.1 (2019-10-13)
@ -3719,6 +3906,12 @@
- The pattern super constructor now sets a `config` property that holds the pattern configuration. This means that unlike before, there is no need to instantiate a pattern to access its config. You can just import the pattern, and it's config property will contain the pattern config. - The pattern super constructor now sets a `config` property that holds the pattern configuration. This means that unlike before, there is no need to instantiate a pattern to access its config. You can just import the pattern, and it's config property will contain the pattern config.
### i18n
#### Added
- Added translations for Penelope, Waralee, and Simone
### utils ### utils
#### Added #### Added
@ -3777,6 +3970,12 @@
- [#102](https://github.com/freesewing/freesewing.org/issues/102): Fixed 'Snippets not defined' error when drafting a seperate button placket - [#102](https://github.com/freesewing/freesewing.org/issues/102): Fixed 'Snippets not defined' error when drafting a seperate button placket
- [#103](https://github.com/freesewing/freesewing.org/issues/103): Fixed 'hemSa not defined' when drafting paperless Simon without seam allowance - [#103](https://github.com/freesewing/freesewing.org/issues/103): Fixed 'hemSa not defined' when drafting paperless Simon without seam allowance
### i18n
#### Added
- [#90](https://github.com/freesewing/freesewing/issues/90): Added missing option translations for Benjamin, Florent, Sandy, Shin, and Theo
### utils ### utils
#### Fixed #### Fixed
@ -3980,6 +4179,12 @@
### core ### core
#### Added
- Initial release
### i18n
#### Added #### Added
- Initial release - Initial release

View file

@ -2,6 +2,7 @@
"collection": "All FreeSewing designs bundles into one pacakge, our collection", "collection": "All FreeSewing designs bundles into one pacakge, our collection",
"config": "Various configurations for FreeSewing", "config": "Various configurations for FreeSewing",
"core": "A library for creating made-to-measure sewing patterns", "core": "A library for creating made-to-measure sewing patterns",
"i18n": "Translation for the FreeSewing project",
"models": "Body measurements data for a range of default sizes", "models": "Body measurements data for a range of default sizes",
"new-design": "Initializer package for a new FreeSewing design: npx @freesewing/new-design", "new-design": "Initializer package for a new FreeSewing design: npx @freesewing/new-design",
"prettier-config": "FreeSewing's shared configuration for prettier", "prettier-config": "FreeSewing's shared configuration for prettier",

View file

@ -119,3 +119,16 @@ export const designs = {
} }
export const collection = Object.keys(designs) export const collection = Object.keys(designs)
export const requiredMeasurements = {}
export const optionalMeasurements = {}
export const measurements = {}
for (const design in designs) {
requiredMeasurements[design] = designs[design].patternConfig.measurements
optionalMeasurements[design] = designs[design].patternConfig.optionalMeasurements
measurements[design] = [
...designs[design].patternConfig.measurements,
...designs[design].patternConfig.optionalMeasurements,
]
}

222
packages/i18n/CHANGELOG.md Normal file
View file

@ -0,0 +1,222 @@
# Change log for: @freesewing/i18n
## 3.0.0 (2023-09-30)
### Changed
- All FreeSewing packages are now ESM only.
- All FreeSewing packages now use named exports.
- Dropped support for NodeJS 14. NodeJS 18 (LTS/hydrogen) or more recent is now required.
## 2.21.0 (2022-06-27)
### Changed
- Migrated from Rollup to Esbuild for all builds
### Fixed
- Added missing lab namespace for English
## 2.20.2 (2022-01-27)
### Fixed
- Patterns options were always in English due to symlinks being used
## 2.20.0 (2022-01-24)
### Fixed
- Fixed issue that was causing plugin translations to always be in English
## 2.19.0 (2021-10-17)
### Fixed
- Fixed bug in resolving of shared pattern options
- Removed optional chaining which broke node v12 support
## 2.18.0 (2021-09-09)
### Added
- Added translations for Yuri
### Fixed
- Added optional chaining so missing options always lead to clear error message
## 2.17.3 (2021-08-16)
### Added
- New translations
## 2.17.2 (2021-08-15)
### Added
- Added new ffsa option for simon & simone
## 2.17.0 (2021-07-01)
### Changed
- Changed antman references to antperson
## 2.16.2 (2021-05-05)
### Changed
- String updates
## 2.16.1 (2021-05-30)
### Added
- New translations for pattern filter
## 2.16.0 (2021-05-24)
### Changed
- Changes to cfp strings
## 2.15.0 (2021-04-15)
### Added
- Added translation for new Titan options
- Added translations for Charlie
## 2.14.0 (2021-03-07)
### Added
- Added translations for Cornelius
## 2.13.0 (2021-02-13)
### Added
- Translation for Hortensia
## 2.11.0 (2021-01-10)
### Changed
- New strings for new features
### Fixed
- Type in Simon title
## 2.10.5 (2020-11-14)
### Fixed
- Added missing `cty.` translations to non-English language files
## 2.9.0 (2020-10-02)
### Added
- Added translations for plugin-title
- Added translations for teagan
- Added some translations for the UI
### Fixed
- Replaced a few identical files with symlinks
## 2.7.0 (2020-07-12)
### Changed
- Added translations for Titan
- Removed `Circumference` suffix from measurement names
## 2.6.0 (2020-05-01)
### Changed
- Changes to support the renaming of @freesewing/fu to @freesewing/florence
## 2.5.0 (2020-04-05)
### Added
- title, description, and options for Dianna
## 2.4.6 (2020-03-23)
### Fixed
- Fixed an bug in the i18n package
## 2.4.3 (2020-03-12)
### Added
- Added more translations
## 2.4.2 (2020-03-08)
### Added
- Added more strings
## 2.2.0 (2020-02-22)
### Added
- Added translations for Breanna
### Changed
- Added/Updated strings for the 2.2 frontend changes
- Changed `Joost De Cock` to `Joost` because spam filters don't like cock
### Removed
- Removed the files for homepage translation, and moved that content to markdown
- Removed the files for editor translation, as it is no longer used
## 2.1.3 (2019-10-18)
### Added
- More translated strings
## 2.1.2 (2019-10-14)
### Fixed
- Fixed issue where symlinks were causing all languages to export English strings
## 2.1.0 (2019-10-06)
### Added
- Added translations for Penelope, Waralee, and Simone
## 2.0.2 (2019-09-06)
### Added
- [#90](https://github.com/freesewing/freesewing/issues/90): Added missing option translations for Benjamin, Florent, Sandy, Shin, and Theo
## 2.0.0 (2019-08-25)
### Added
- Initial release
This is the **initial release**, and the start of this change log.
> Prior to version 2, FreeSewing was not a JavaScript project.
> As such, that history is out of scope for this change log.

162
packages/i18n/README.md Normal file
View file

@ -0,0 +1,162 @@
<p align='center'><a
href="https://www.npmjs.com/package/@freesewing/i18n"
title="@freesewing/i18n on NPM"
><img src="https://img.shields.io/npm/v/@freesewing/i18n.svg"
alt="@freesewing/i18n on NPM"/>
</a><a
href="https://opensource.org/licenses/MIT"
title="License: MIT"
><img src="https://img.shields.io/npm/l/@freesewing/i18n.svg?label=License"
alt="License: MIT"/>
</a><a
href="https://deepscan.io/dashboard#view=project&tid=2114&pid=2993&bid=23256"
title="Code quality on DeepScan"
><img src="https://deepscan.io/api/teams/2114/projects/2993/branches/23256/badge/grade.svg"
alt="Code quality on DeepScan"/>
</a><a
href="https://github.com/freesewing/freesewing/issues?q=is%3Aissue+is%3Aopen+label%3Apkg%3Ai18n"
title="Open issues tagged pkg:i18n"
><img src="https://img.shields.io/github/issues/freesewing/freesewing/pkg:i18n.svg?label=Issues"
alt="Open issues tagged pkg:i18n"/>
</a><a
href="#contributors-"
title="All Contributors"
><img src="https://img.shields.io/badge/all_contributors-131-pink.svg"
alt="All Contributors"/>
</a></p><p align='center'><a
href="https://twitter.com/freesewing_org"
title="Follow @freesewing_org on Twitter"
><img src="https://img.shields.io/badge/%F3%A0%80%A0-Follow%20us-blue.svg?logo=twitter&logoColor=white&logoWidth=15"
alt="Follow @freesewing_org on Twitter"/>
</a><a
href="https://chat.freesewing.org"
title="Chat with us on Discord"
><img src="https://img.shields.io/discord/698854858052075530?label=Chat%20on%20Discord"
alt="Chat with us on Discord"/>
</a><a
href="https://freesewing.org/patrons/join"
title="Become a FreeSewing Patron"
><img src="https://img.shields.io/badge/%F3%A0%80%A0-Support%20us-blueviolet.svg?logo=cash-app&logoColor=white&logoWidth=15"
alt="Become a FreeSewing Patron"/>
</a><a
href="https://instagram.com/freesewing_org"
title="Follow @freesewing_org on Twitter"
><img src="https://img.shields.io/badge/%F3%A0%80%A0-Follow%20us-E4405F.svg?logo=instagram&logoColor=white&logoWidth=15"
alt="Follow @freesewing_org on Twitter"/>
</a></p>
# @freesewing/i18n
Translation for the FreeSewing project
# FreeSewing
> [!TIP]
>#### Support FreeSewing: Become a patron, or make a one-time donation 🥰
>
> FreeSewing is an open source project maintained by Joost De Cock and financially supported by the FreeSewing patrons.
>
> If you feel FreeSewing is worthwhile, and you can spend a few coins without
hardship, then you should [join us and become a patron](https://freesewing.org/community/join).
## What am I looking at? 🤔
This repository is the FreeSewing *monorepo* holding all FreeSewing's websites, documentation, designs, plugins, and other NPM packages.
This folder holds: @freesewing/i18n
If you're not entirely sure what to do or how to start, type this command:
```
npm run tips
```
> [!NOTE]
> If you don't want to set up a dev environment, you can run it in your browser:
>
> [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/freesewing/freesewing)
>
> We recommend that you fork our repository and then
> put `gitpod.io/#<entire-url-of-your-fork` into a browser
> to start up a browser-based dev environment of your own.
## About FreeSewing 💀
Where the world of makers and developers collide, that's where you'll find FreeSewing.
If you're a maker, checkout [freesewing.org](https://freesewing.org/) where you can generate
sewing patterns adapted to your measurements.
If you're a developer, the FreeSewing documentation lives at [freesewing.dev](https://freesewing.dev/).
The FreeSewing [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox
for parametric design of sewing patterns. But FreeSewing also provides a range
of [plugins](https://freesewing.dev/reference/plugins/) that further extend the
functionality of the platform.
If you have NodeJS installed, you can try it right now by running:
```bash
npx @freesewing/new-design
```
Getting started guides are available for:
- [Linux](https://freesewing.dev/tutorials/getting-started-linux/)
- [MacOS](https://freesewing.dev/tutorials/getting-started-mac/)
- [Windows](https://freesewing.dev/tutorials/getting-started-windows/)
The [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) will
show you how to create your first parametric design.
## Getting started ⚡
To get started with FreeSewing, you can spin up our development environment with:
```bash
npx @freesewing/new-design
```
To work with FreeSewing's monorepo, you'll need [NodeJS v20](https://nodejs.org) on your system.
Once you have that, clone (or fork) this repo and run `npm run kickstart`:
```bash
git clone git@github.com:freesewing/freesewing.git
cd freesewing
npm run kickstart
```
## Links 👩‍💻
**Official channels**
- 💻 Makers website: [FreeSewing.org](https://freesewing.org)
- 💻 Developers website: [FreeSewing.dev](https://freesewing.dev)
- ✅ [Support](https://github.com/freesewing/freesewing/issues/new/choose),
[Issues](https://github.com/freesewing/freesewing/issues) &
[Discussions](https://github.com/freesewing/freesewing/discussions) on
[GitHub](https://github.com/freesewing/freesewing)
**Social media**
- 🐦 Twitter: [@freesewing_org](https://twitter.com/freesewing_org)
- 📷 Instagram: [@freesewing_org](https://instagram.com/freesewing_org)
**Places the FreeSewing community hangs out**
- 💬 [Discord](https://discord.freesewing.org/)
- 💬 [Facebook](https://www.facebook.com/groups/627769821272714/)
- 💬 [Reddit](https://www.reddit.com/r/freesewing/)
## License: MIT 🤓
© [Joost De Cock](https://github.com/joostdecock).
See [the license file](https://github.com/freesewing/freesewing/blob/develop/LICENSE) for details.
## Where to get help 🤯
For [Support](https://github.com/freesewing/freesewing/issues/new/choose),
please use the [Issues](https://github.com/freesewing/freesewing/issues) &
[Discussions](https://github.com/freesewing/freesewing/discussions) on
[GitHub](https://github.com/freesewing/freesewing).

4
packages/i18n/data.mjs Normal file
View file

@ -0,0 +1,4 @@
// This file is auto-generated | All changes you make will be overwritten.
export const name = '@freesewing/i18n'
export const version = '3.3.0-rc.1'
export const data = { name, version }

View file

@ -0,0 +1,50 @@
{
"name": "@freesewing/i18n",
"version": "3.3.0-rc.1",
"description": "Translation for the FreeSewing project",
"author": "Joost De Cock <joost@joost.at> (https://github.com/joostdecock)",
"homepage": "https://freesewing.org/",
"repository": "github:freesewing/freesewing",
"license": "MIT",
"bugs": {
"url": "https://github.com/freesewing/freesewing/issues"
},
"funding": {
"type": "individual",
"url": "https://freesewing.org/patrons/join"
},
"keywords": [
"freesewing",
"i18n",
"internationalisation",
"languages",
"localisation",
"translation"
],
"type": "module",
"module": "src/index.mjs",
"exports": {
".": "./src/index.mjs"
},
"scripts": {
"symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -",
"test": "echo \"i18n: No tests configured. Perhaps you could write some?\" && exit 0",
"tips": "node ../../scripts/help.mjs",
"lint": "npx eslint 'src/**' 'tests/*.mjs'"
},
"peerDependencies": {},
"dependencies": {},
"devDependencies": {},
"files": [
"src/**",
"README.md"
],
"publishConfig": {
"access": "public",
"tag": "next"
},
"engines": {
"node": ">= 20"
},
"private": true
}

View file

@ -0,0 +1,3 @@
import { measurements } from './measurements.mjs'
export { measurements }

View file

@ -0,0 +1,40 @@
export const measurements = {
ankle: 'Ankle circumference',
biceps: 'Biceps circumference',
bustFront: 'Bust front',
bustPointToUnderbust: 'Bust point to underbust',
bustSpan: 'Bust span',
chest: 'Chest circumference',
crossSeam: 'Cross seam',
crossSeamFront: 'Cross seam front',
head: 'Head circumference',
heel: 'Heel circumference',
highBustFront: 'High bust front',
highBust: 'High bust',
hips: 'Hips circumference',
hpsToBust: 'HPS to bust',
hpsToWaistBack: 'HPS to waist back',
hpsToWaistFront: 'HPS to waist front',
inseam: 'Inseam',
knee: 'Knee circumference',
neck: 'Neck circumference',
seat: 'Seat circumference',
seatBack: 'Seat back',
crotchDepth: 'Crotch depth',
shoulderSlope: 'Shoulder slope',
shoulderToElbow: 'Shoulder to elbow',
shoulderToShoulder: 'Shoulder to shoulder',
shoulderToWrist: 'Shoulder to wrist',
underbust: 'Underbust',
upperLeg: 'Upper leg circumference',
waist: 'Waist circumference',
waistBack: 'Waist back',
waistToArmpit: 'Waist to armpit',
waistToFloor: 'Waist to floor',
waistToHips: 'Waist to hips',
waistToKnee: 'Waist to knee',
waistToSeat: 'Waist to seat',
waistToUnderbust: 'Waist to underbust',
waistToUpperLeg: 'Waist to upper leg',
wrist: 'Wrist circumference',
}

View file

@ -1,11 +1,19 @@
// Dependencies // Dependencies
import { measurements, isDegreeMeasurement, control as controlConfig } from '@freesewing/config' import {
measurements,
isDegreeMeasurement,
control as controlConfig,
urls,
} from '@freesewing/config'
import { measurements as measurementTranslations } from '@freesewing/i18n'
import { measurements as designMeasurements } from '@freesewing/collection'
import { import {
cloudflareImageUrl, cloudflareImageUrl,
capitalize, capitalize,
formatMm, formatMm,
horFlexClasses, horFlexClasses,
linkClasses, linkClasses,
notEmpty,
roundDistance, roundDistance,
shortDate, shortDate,
timeAgo, timeAgo,
@ -18,8 +26,10 @@ import React, { useState, useEffect, Fragment, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount' import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend' import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components // Components
import { Link as WebLink } from '@freesewing/react/components/Link' import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link'
import { import {
BoolNoIcon,
BoolYesIcon,
CloneIcon, CloneIcon,
CuratedMeasurementsSetIcon, CuratedMeasurementsSetIcon,
EditIcon, EditIcon,
@ -36,54 +46,26 @@ import {
// BoolNoIcon, // BoolNoIcon,
} from '@freesewing/react/components/Icon' } from '@freesewing/react/components/Icon'
import { BookmarkButton, MsetCard } from '@freesewing/react/components/Account' import { BookmarkButton, MsetCard } from '@freesewing/react/components/Account'
import { ToggleInput } from '@freesewing/react/components/Input' import {
DesignInput,
MarkdownInput,
ListInput,
MeasieInput,
PassiveImageInput,
StringInput,
ToggleInput,
} from '@freesewing/react/components/Input'
import { DisplayRow } from './shared.mjs' import { DisplayRow } from './shared.mjs'
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import { ModalWrapper } from '@freesewing/react/components/Modal' import { ModalWrapper } from '@freesewing/react/components/Modal'
import { Json } from '@freesewing/react/components/Json' import { Json } from '@freesewing/react/components/Json'
import { Yaml } from '@freesewing/react/components/Yaml' import { Yaml } from '@freesewing/react/components/Yaml'
import { Popout } from '@freesewing/react/components/Popout'
//import { measurements as designMeasurements } from 'shared/prebuild/data/design-measurements.mjs' const t = (input) => {
//import { freeSewingConfig as conf, controlLevels } from 'shared/config/freesewing.config.mjs' console.log('t called', input)
//import { isDegreeMeasurement } from 'config/measurements.mjs' return input
//import { }
// shortDate,
// cloudflareImageUrl,
// formatMm,
// hasRequiredMeasurements,
// capitalize,
// horFlexClasses,
//} from 'shared/utils.mjs'
//// Hooks
//import { useState, useEffect, useContext } from 'react'
//import { useTranslation } from 'next-i18next'
//import { useAccount } from 'shared/hooks/use-account.mjs'
//import { useBackend } from 'shared/hooks/use-backend.mjs'
//import { useRouter } from 'next/router'
//// Context
//import { LoadingStatusContext } from 'shared/context/loading-status-context.mjs'
//import { ModalContext } from 'shared/context/modal-context.mjs'
//// Components
//import { Popout } from 'shared/components/popout/index.mjs'
//import { BackToAccountButton } from './shared.mjs'
//import { AnchorLink, PageLink, Link } from 'shared/components/link.mjs'
//import { Json } from 'shared/components/json.mjs'
//import { Yaml } from 'shared/components/yaml.mjs'
//import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
//import { Mdx } from 'shared/components/mdx/dynamic.mjs'
//import Timeago from 'react-timeago'
//import {
// StringInput,
// ToggleInput,
// PassiveImageInput,
// ListInput,
// MarkdownInput,
// MeasieInput,
// DesignDropdown,
// ns as inputNs,
//} from 'shared/components/inputs.mjs'
//import { BookmarkButton } from 'shared/components/bookmarks.mjs'
//import { DynamicMdx } from 'shared/components/mdx/dynamic.mjs'
/* /*
* Component to show an individual measurements set * Component to show an individual measurements set
@ -163,10 +145,8 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
} }
}, [id, publicOnly]) }, [id, publicOnly])
const filterMeasurements = () => { const filterMeasurements = () =>
if (!filter) return measurements.map((m) => `measurements:${m}` + `|${m}`).sort() filter ? designMeasurements[filter].sort() : measurements.sort()
else return designMeasurements[filter].map((m) => `measurements:${m}` + `|${m}`).sort()
}
if (!id || !mset) return null if (!id || !mset) return null
@ -192,7 +172,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
setLoadingStatus([true, 'Saving measurements set']) setLoadingStatus([true, 'Saving measurements set'])
const [status, body] = await backend.updateSet(mset.id, data) const [status, body] = await backend.updateSet(mset.id, data)
if (status === 200 && body.result === 'success') { if (status === 200 && body.result === 'success') {
setMset(body.data.set) setMset(body.set)
setEdit(false) setEdit(false)
setLoadingStatus([true, 'Nailed it', true, true]) setLoadingStatus([true, 'Nailed it', true, true])
} else } else
@ -218,10 +198,9 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
} }
delete data.img delete data.img
const [status, body] = await backend.createSet(data) const [status, body] = await backend.createSet(data)
if (status === 200 && body.result === 'success') { if (status === 201 && body.result === 'created') {
setMset(body.data.set) setLoadingStatus([true, 'Loading newly created set', true, true])
setEdit(false) window.location = `/account/set/?id=${body.set.id}`
setLoadingStatus([true, 'Nailed it', true, true])
} else setLoadingStatus([true, 'We failed to create this measurements set', true, false]) } else setLoadingStatus([true, 'We failed to create this measurements set', true, false])
} }
@ -235,14 +214,14 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
{account.control > 2 && mset.public && mset.userId !== account.id ? ( {account.control > 2 && mset.public && mset.userId !== account.id ? (
<div className="flex flex-row gap-2 items-center"> <div className="flex flex-row gap-2 items-center">
<a <a
className="badge badge-secondary font-bold badge-lg" className="daisy-badge daisy-badge-secondary font-bold daisy-badge-lg"
href={`${conf.backend}/sets/${mset.id}.json`} href={`${urls.backend}/sets/${mset.id}.json`}
> >
JSON JSON
</a> </a>
<a <a
className="badge badge-success font-bold badge-lg" className="daisy-badge daisy-badge-success font-bold daisy-badge-lg"
href={`${conf.backend}/sets/${mset.id}.yaml`} href={`${urls.backend}/sets/${mset.id}.yaml`}
> >
YAML YAML
</a> </a>
@ -253,7 +232,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
{account.control > 3 && mset.userId === account.id ? ( {account.control > 3 && mset.userId === account.id ? (
<div className="flex flex-row gap-2 items-center"> <div className="flex flex-row gap-2 items-center">
<button <button
className="badge badge-secondary font-bold badge-lg" className="daisy-badge daisy-badge-secondary font-bold daisy-badge-lg"
onClick={() => onClick={() =>
setModal( setModal(
<ModalWrapper keepOpenOnClick> <ModalWrapper keepOpenOnClick>
@ -265,7 +244,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
JSON JSON
</button> </button>
<button <button
className="badge badge-success font-bold badge-lg" className="daisy-badge daisy-badge-success font-bold daisy-badge-lg text-neutral-content"
onClick={() => onClick={() =>
setModal( setModal(
<ModalWrapper keepOpenOnClick> <ModalWrapper keepOpenOnClick>
@ -283,12 +262,12 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
{account.id && account.control > 2 && mset.public && mset.userId !== account.id ? ( {account.id && account.control > 2 && mset.public && mset.userId !== account.id ? (
<button <button
className="daisy-btn daisy-btn-primary" className="daisy-btn daisy-btn-primary"
title={t('account:importSet')} title="Import measurements set"
onClick={importSet} onClick={importSet}
> >
<div className="flex flex-row gap-4 justify-between items-center w-full"> <div className="flex flex-row gap-4 justify-between items-center w-full">
<UploadIcon /> <UploadIcon />
{t('account:importSet')} Import measurements set
</div> </div>
</button> </button>
) : null} ) : null}
@ -379,7 +358,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
return ( return (
<div className="max-w-2xl"> <div className="max-w-2xl">
{heading} {heading}
<SuggestCset {...{ mset, setLoadingStatus, backend, t }} /> <SuggestCset {...{ mset, setLoadingStatus, backend, Link }} />
</div> </div>
) )
@ -466,56 +445,27 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
return ( return (
<div className="max-w-2xl"> <div className="max-w-2xl">
{heading} {heading}
<ul className="list list-disc list-inside ml-4"> <h2 id="measies">Measurements</h2>
{['measies', 'data'].map((s) => (
<li key={s}>
<AnchorLink id={s}>{s}</AnchorLink>
</li>
))}
<ul className="list list-disc list-inside ml-4">
<li>
<AnchorLink id="name">Name</AnchorLink>
</li>
{account.control >= conf.account.sets.img ? (
<li>
<AnchorLink id="image">Image</AnchorLink>
</li>
) : null}
{['public', 'units', 'notes'].map((id) =>
account.control >= conf.account.sets[id] ? (
<li key={id}>
<AnchorLink id="units">{id}</AnchorLink>
</li>
) : null
)}
</ul>
</ul>
<h2 id="measies">{t('measies')}</h2>
<div className="bg-secondary px-4 pt-1 pb-4 rounded-lg shadow bg-opacity-10"> <div className="bg-secondary px-4 pt-1 pb-4 rounded-lg shadow bg-opacity-10">
<DesignDropdown <DesignInput
update={setFilter} update={setFilter}
label="Filter by design" label="Filter by design"
current={filter} current={filter}
firstOption={<option value="">Clear filter</option>} firstOption={<option value="">Clear filter</option>}
/> />
</div> </div>
{filterMeasurements().map((mplus) => { {filterMeasurements().map((m) => (
const [translated, m] = mplus.split('|') <MeasieInput
id={`measie-${m}`}
return ( key={m}
<MeasieInput m={m}
id={`measie-${m}`} imperial={mset.imperial}
key={m} label={measurementTranslations[m]}
m={m} current={mset.measies[m]}
imperial={mset.imperial} original={mset.measies[m]}
label={translated} update={updateMeasies}
current={mset.measies[m]} />
original={mset.measies[m]} ))}
update={updateMeasies}
/>
)
})}
<h2 id="data">Data</h2> <h2 id="data">Data</h2>
@ -533,7 +483,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
{/* img: Control level determines whether or not to show this */} {/* img: Control level determines whether or not to show this */}
<span id="image"></span> <span id="image"></span>
{account.control >= conf.account.sets.img ? ( {account.control >= controlConfig.account.sets.img ? (
<PassiveImageInput <PassiveImageInput
id="set-img" id="set-img"
label="Image" label="Image"
@ -545,7 +495,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
{/* public: Control level determines whether or not to show this */} {/* public: Control level determines whether or not to show this */}
<span id="public"></span> <span id="public"></span>
{account.control >= conf.account.sets.public ? ( {account.control >= controlConfig.account.sets.public ? (
<ListInput <ListInput
id="set-public" id="set-public"
label="Public" label="Public"
@ -581,7 +531,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
{/* units: Control level determines whether or not to show this */} {/* units: Control level determines whether or not to show this */}
<span id="units"></span> <span id="units"></span>
{account.control >= conf.account.sets.units ? ( {account.control >= controlConfig.account.sets.units ? (
<> <>
<ListInput <ListInput
id="set-units" id="set-units"
@ -611,13 +561,15 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
]} ]}
current={imperial} current={imperial}
/> />
<span className="text-large text-warning">{t('unitsMustSave')}</span> <span className="text-large text-warning">
Note: You must save after changing Units to have the change take effect on this page.
</span>
</> </>
) : null} ) : null}
{/* notes: Control level determines whether or not to show this */} {/* notes: Control level determines whether or not to show this */}
<span id="notes"></span> <span id="notes"></span>
{account.control >= conf.account.sets.notes ? ( {account.control >= controlConfig.account.sets.notes ? (
<MarkdownInput <MarkdownInput
id="set-notes" id="set-notes"
label="Notes" label="Notes"
@ -652,14 +604,137 @@ export const MeasurementValue = ({ val, m, imperial = false }) =>
<span dangerouslySetInnerHTML={{ __html: formatMm(val, imperial) }}></span> <span dangerouslySetInnerHTML={{ __html: formatMm(val, imperial) }}></span>
) )
/* /**
* React component to suggest a measurements set for curation
*
* @param {object} props - All React props
* @param {string} mset - The measurements set
*/
export const SuggestCset = ({ mset, Link }) => {
// State
const [height, setHeight] = useState('')
const [img, setImg] = useState('')
const [name, setName] = useState('')
const [notes, setNotes] = useState('')
const [submission, setSubmission] = useState(false)
console.log(mset)
// Hooks
const backend = useBackend()
// Method to submit the form
const suggestSet = async () => {
setLoadingStatus([true, 'Contacting backend'])
const result = await backend.suggestCset({ set: mset.id, height, img, name, notes })
if (result.success && result.data.submission) {
setSubmission(result.data.submission)
setLoadingStatus([true, 'Nailed it', true, true])
} else setLoadingStatus([true, 'An unexpected error occured. Please report this.', true, false])
}
const missing = []
for (const m of measurements) {
if (typeof mset.measies[m] === 'undefined') missing.push(m)
}
if (submission) {
const url = `/curate/sets/suggested/${submission.id}`
return (
<>
<h2>Thank you</h2>
<p>Your submission has been registered and will be processed by one of our curators.</p>
<p>
It is available at: <Link href={url}>{url}</Link>
</p>
</>
)
}
return (
<>
<h2>Suggest a measurements set for curation</h2>
<h4 className="flex flex-row items-center gap-2">
{missing.length > 0 ? <BoolNoIcon /> : <BoolYesIcon />}
Measurements
</h4>
{missing.length > 0 ? (
<>
<p>
To ensure curated measurements sets work for all designs, you need to provide a full set
of measurements.
</p>
<p>Your measurements set is missing the following measurements:</p>
<ul className="list list-inside list-disc ml-4">
{missing.map((m) => (
<li key={m}>{m}</li>
))}
</ul>
</>
) : (
<p>All measurements are available.</p>
)}
<h4 className="flex flex-row items-center gap-2">
{name.length > 1 ? <BoolYesIcon /> : <BoolNoIcon />}
Name
</h4>
<p>Each curated set has a name. You can suggest your own name or a pseudonym.</p>
<StringInput label="Name" current={name} update={setName} valid={(val) => val.length > 1} />
<h4 className="flex flex-row items-center gap-2">
{height.length > 1 ? <BoolYesIcon /> : <BoolNoIcon />}
Height
</h4>
<p>
To allow organizing and presenting our curated sets in a structured way, we organize them by
height.
</p>
<StringInput
label="height"
current={height}
update={setHeight}
valid={(val) => val.length > 1}
/>
<h4 className="flex flex-row items-center gap-2 mt-4">
{img.length > 0 ? <BoolYesIcon /> : <BoolNoIcon />}
Image
</h4>
<p>
Finally, we need a picture. Please refer to the documentation to see what makes a good
picture for a curated measurements set.
<Link href="/docs/about/site/csets">Documentation</Link>
</p>
<PassiveImageInput
label="Image"
current={img}
update={setImg}
valid={(val) => val.length > 1}
/>
<h4 className="flex flex-row items-center gap-2 mt-4">
<BoolYesIcon />
Notes
</h4>
<p>If you would like to add any notes, you can do so here.</p>
<Popout tip compact>
This field supports markdown
</Popout>
<MarkdownInput label="Notes" current={notes} update={setNotes} valid={() => true} />
<button
className="daisy-btn daisy-btn-primary w-full mt-4"
disabled={!(missing.length === 0 && height.length > 1 && img.length > 0)}
onClick={suggestSet}
>
Suggest for curation
</button>
</>
)
}
export const NewSet = () => { export const NewSet = () => {
// Hooks // Hooks
const { setLoadingStatus } = useContext(LoadingStatusContext)
const backend = useBackend() const backend = useBackend()
const { t } = useTranslation(ns)
const router = useRouter()
const { account } = useAccount() const { account } = useAccount()
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
// State // State
const [name, setName] = useState('') const [name, setName] = useState('')
@ -669,39 +744,46 @@ export const NewSet = () => {
// Helper method to create a new set // Helper method to create a new set
const createSet = async () => { const createSet = async () => {
setLoadingStatus([true, 'processingUpdate']) setLoadingStatus([true, 'Storing new measurements set'])
const result = await backend.createSet({ name, imperial }) const [status, body] = await backend.createSet({ name, imperial })
if (result.success) { if (status === 201 && body.result === 'created') {
setLoadingStatus([true, t('nailedIt'), true, true]) setLoadingStatus([true, 'Nailed it', true, true])
router.push(`/account/set?id=${result.data.set.id}`) window.location = `/account/set?id=${body.set.id}`
} else setLoadingStatus([true, 'backendError', true, false]) } else
setLoadingStatus([
true,
'Failed to save the measurments set. Please report this.',
true,
false,
])
} }
return ( return (
<div className="max-w-xl"> <div className="max-w-xl">
<h5>{t('name')}</h5> <h5>Name</h5>
<p>{t('setNameDesc')}</p> <p>Give this set of measurements a name. That will help tell them apart.</p>
<input <StringInput
autoFocus id="new-set"
value={name} label="Name"
onChange={(evt) => setName(evt.target.value)} update={setName}
className="input w-full input-bordered flex flex-row" current={name}
type="text" valid={(val) => val && val.length > 0}
placeholder={'Georg Cantor'} placeholder={'Georg Cantor'}
/> />
<div className="flex flex-row gap-2 items-center w-full mt-8 mb-2"> <div className="flex flex-row gap-2 items-center w-full mt-8 mb-2">
<button <button
className="btn btn-primary grow capitalize" className="daisy-btn daisy-btn-primary grow capitalize"
disabled={name.length < 1} disabled={name.length < 1}
onClick={createSet} onClick={createSet}
> >
{t('newSet')} New Measurements Set
</button> </button>
</div> </div>
</div> </div>
) )
} }
/*
export const SetCard = ({ export const SetCard = ({
set, set,
@ -950,122 +1032,5 @@ export const BookmarkedSetPicker = ({ design, clickHandler, t, size, href }) =>
) )
} }
const SuggestCset = ({ mset, backend, setLoadingStatus, t }) => {
// State
const [height, setHeight] = useState('')
const [img, setImg] = useState('')
const [name, setName] = useState('')
const [notes, setNotes] = useState('')
const [submission, setSubmission] = useState(false)
// Method to submit the form
const suggestSet = async () => {
setLoadingStatus([true, 'status:contactingBackend'])
const result = await backend.suggestCset({ set: mset.id, height, img, name, notes })
if (result.success && result.data.submission) {
setSubmission(result.data.submission)
setLoadingStatus([true, 'status:nailedIt', true, true])
} else setLoadingStatus([true, 'backendError', true, false])
}
const missing = []
for (const m of measurements) {
if (typeof mset.measies[m] === 'undefined') missing.push(m)
}
if (submission) {
const url = `/curate/sets/suggested/${submission.id}`
return (
<>
<h2>{t('account:thankYouVeryMuch')}</h2>
<p>{t('account:csetSuggestedMsg')}</p>
<p>
{t('account:itIsAvailableAt')}: <PageLink href={url} txt={url} />
</p>
</>
)
}
return (
<>
<h2>{t('account:suggestCset')}</h2>
<h4 className="flex flex-row items-center gap-2">
{missing.length > 0 ? <BoolNoIcon /> : <BoolYesIcon />}
{t('account:measurements')}
</h4>
{missing.length > 0 ? (
<>
<p>{t('account:csetAllMeasies')}</p>
<p>{t('account:csetMissing')}:</p>
<ul className="list list-inside list-disc ml-4">
{missing.map((m) => (
<li key={m}>{t(`measurements:${m}`)}</li>
))}
</ul>
</>
) : (
<p>{t('account:allMeasiesAvailable')}</p>
)}
<h4 className="flex flex-row items-center gap-2">
{name.length > 1 ? <BoolYesIcon /> : <BoolNoIcon />}
{t('account:name')}
</h4>
<p>{t('account:csetNameMsg')}</p>
<StringInput
label={t('account:name')}
current={name}
update={setName}
valid={(val) => val.length > 1}
/>
<h4 className="flex flex-row items-center gap-2">
{height.length > 1 ? <BoolYesIcon /> : <BoolNoIcon />}
{t('measurements:height')}
</h4>
<p>{t('account:csetHeightMsg1')}</p>
<StringInput
label={t('measurements:height')}
current={height}
update={setHeight}
valid={(val) => val.length > 1}
/>
<h4 className="flex flex-row items-center gap-2 mt-4">
{img.length > 0 ? <BoolYesIcon /> : <BoolNoIcon />}
{t('account:img')}
</h4>
<p>
{t('account:csetImgMsg')}:{' '}
<PageLink href="/docs/about/site/csets">{t('account:docs')}</PageLink>
</p>
<PassiveImageInput
label={t('account:img')}
current={img}
update={setImg}
valid={(val) => val.length > 1}
/>
<h4 className="flex flex-row items-center gap-2 mt-4">
<BoolYesIcon />
{t('account:notes')}
</h4>
<p>{t('account:csetNotesMsg')}</p>
<Popout tip compact>
{t('account:mdSupport')}
</Popout>
<MarkdownInput
label={t('account:notes')}
current={notes}
update={setNotes}
valid={() => true}
/>
<button
className="btn btn-primary w-full mt-4"
disabled={!(missing.length === 0 && height.length > 1 && img.length > 0)}
onClick={suggestSet}
>
{t('account:suggestForCuration')}
</button>
</>
)
}
*/ */

View file

@ -2,7 +2,7 @@ import React from 'react'
import { Bookmarks, BookmarkButton } from './Bookmarks.mjs' import { Bookmarks, BookmarkButton } from './Bookmarks.mjs'
import { Links } from './Links.mjs' import { Links } from './Links.mjs'
import { Set } from './Set.mjs' import { Set, NewSet } from './Set.mjs'
import { Sets, MsetCard } from './Sets.mjs' import { Sets, MsetCard } from './Sets.mjs'
export { Bookmarks, BookmarkButton, Links, Set, Sets, MsetCard } export { Bookmarks, BookmarkButton, Links, Set, NewSet, Sets, MsetCard }

View file

@ -1,26 +1,34 @@
import React, { useState } from 'react' import React, { useContext, useState } from 'react'
import ReactDOMServer from 'react-dom/server' import ReactDOMServer from 'react-dom/server'
import { CopyIcon, OkIcon } from '@freesewing/react/components/Icon' import { CopyIcon, OkIcon } from '@freesewing/react/components/Icon'
import { CopyToClipboard as Copy } from 'react-copy-to-clipboard' import { CopyToClipboard as Copy } from 'react-copy-to-clipboard'
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
const strip = (html) => const strip = (html) =>
typeof DOMParser === 'undefined' typeof DOMParser === 'undefined'
? html ? html
: new DOMParser().parseFromString(html, 'text/html').body.textContent || '' : new DOMParser().parseFromString(html, 'text/html').body.textContent || ''
const handleCopied = (setCopied) => { const handleCopied = (setCopied, setLoadingStatus, label) => {
setCopied(true) setCopied(true)
setLoadingStatus([
true,
label ? `${label} copied to clipboard` : 'Copied to clipboard',
true,
true,
])
setTimeout(() => setCopied(false), 1000) setTimeout(() => setCopied(false), 1000)
} }
export const CopyToClipboard = ({ content }) => { export const CopyToClipboard = ({ content, label = false }) => {
const [copied, setCopied] = useState(false) const [copied, setCopied] = useState(false)
const { setLoadingStatus } = useContext(LoadingStatusContext)
const text = const text =
typeof content === 'string' ? content : strip(ReactDOMServer.renderToStaticMarkup(content)) typeof content === 'string' ? content : strip(ReactDOMServer.renderToStaticMarkup(content))
return ( return (
<Copy text={text} onCopy={() => handleCopied(setCopied)}> <Copy text={text} onCopy={() => handleCopied(setCopied, setLoadingStatus, label)}>
<button className={copied ? 'text-success' : ''}> <button className={copied ? 'text-success' : ''}>
{copied ? ( {copied ? (
<OkIcon className="w-5 h-5 text-success-content bg-success rounded-full p-1" stroke={4} /> <OkIcon className="w-5 h-5 text-success-content bg-success rounded-full p-1" stroke={4} />

View file

@ -1,25 +1,41 @@
import React from 'react' import React from 'react'
import { CopyToClipboard } from '@freesewing/react/components/CopyToClipboard' import { CopyToClipboard } from '@freesewing/react/components/CopyToClipboard'
const names = { const defaultTitles = {
js: 'Javascript', js: 'Javascript',
bash: 'Bash prompt', bash: 'Bash commands',
sh: 'Shell prompt', sh: 'Shell commands',
json: 'JSON', json: 'JSON',
yaml: 'file.yaml', yaml: 'YAML',
} }
export const Highlight = (props) => { /**
let language = 'txt' * A React component to highlight code
if (props.language) language = props.language *
if (props.children?.props?.className) { * @params {object} props - All React props
language = props.children.props.className.split('-').pop() * @params {string} language - The language to highlight
* @params {object} children - The React children
* @params {bool} raw - Set this to true to not escape tags
* @params {string} title - Title for the highlight
* @params {string} copy - Content to copy to clipboard
*/
export const Highlight = ({
language = 'txt',
children,
raw = false,
title = false,
copy = false,
}) => {
if (children?.props?.className) {
language = children.props.className.split('-').pop()
} }
const preProps = { const preProps = {
className: `language-${language} hljs text-base lg:text-lg whitespace-break-spaces overflow-scroll pr-4`, className: `language-${language} hljs text-base lg:text-lg whitespace-break-spaces overflow-scroll pr-4`,
} }
if (props.raw) preProps.dangerouslySetInnerHTML = { __html: props.raw } if (raw) preProps.dangerouslySetInnerHTML = { __html: raw }
const label = title ? title : defaultTitles[language] ? defaultTitles[language] : language
return ( return (
<div className="hljs my-4"> <div className="hljs my-4">
@ -31,10 +47,10 @@ export const Highlight = (props) => {
px-4 py-1 mb-2 lg:text-sm px-4 py-1 mb-2 lg:text-sm
`} `}
> >
<span>{props.title ? props.title : names[language] ? names[language] : language}</span> <span>{label}</span>
<CopyToClipboard content={props.children} /> <CopyToClipboard content={copy ? copy : children} label={label} />
</div> </div>
<pre {...preProps}>{props.children}</pre> <pre {...preProps}>{children}</pre>
</div> </div>
) )
} }

View file

@ -14,13 +14,11 @@ import React, { useState, useCallback, useContext } from 'react'
import { useDropzone } from 'react-dropzone' import { useDropzone } from 'react-dropzone'
import { useBackend } from '@freesewing/react/hooks/useBackend' import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components // Components
//import { Mdx } from 'shared/components/mdx/dynamic.mjs' import { ResetIcon, UploadIcon } from '@freesewing/react/components/Icon'
import { ResetIcon, DocsIcon, UploadIcon } from '@freesewing/react/components/Icon'
import { ModalWrapper } from '@freesewing/react/components/Modal' import { ModalWrapper } from '@freesewing/react/components/Modal'
import { isDegreeMeasurement } from '@freesewing/config' import { isDegreeMeasurement } from '@freesewing/config'
import { Tabs, Tab } from '@freesewing/react/components/Tab' import { Tabs, Tab } from '@freesewing/react/components/Tab'
import Markdown from 'react-markdown'
export const ns = ['account', 'measurements', 'designs']
/* /*
* Helper component to display a tab heading * Helper component to display a tab heading
@ -32,8 +30,8 @@ export const _Tab = ({
setActiveTab, // Method to set the active tab setActiveTab, // Method to set the active tab
}) => ( }) => (
<button <button
className={`text-lg font-bold capitalize tab tab-bordered grow className={`text-lg font-bold capitalize daisy-tab daisy-tab-bordered grow
${activeTab === id ? 'tab-active' : ''}`} ${activeTab === id ? 'daisy-tab-active' : ''}`}
onClick={() => setActiveTab(id)} onClick={() => setActiveTab(id)}
> >
{label ? label : id} {label ? label : id}
@ -46,42 +44,16 @@ export const _Tab = ({
export const FormControl = ({ export const FormControl = ({
label, // the (top-left) label label, // the (top-left) label
children, // Children to go inside the form control children, // Children to go inside the form control
docs = false, // Optional top-right label
labelBL = false, // Optional bottom-left label labelBL = false, // Optional bottom-left label
labelBR = false, // Optional bottom-right label labelBR = false, // Optional bottom-right label
forId = false, // ID of the for element we are wrapping forId = false, // ID of the for element we are wrapping
}) => { }) => {
const { setModal } = useContext(ModalContext)
if (labelBR && !labelBL) labelBL = <span></span> if (labelBR && !labelBL) labelBL = <span></span>
const topLabelChildren = ( const topLabelChildren = (
<> <span className="daisy-label-text text-sm lg:text-base font-bold mb-1 text-inherit">
<span className="daisy-label-text text-sm lg:text-base font-bold mb-1 text-inherit"> {label}
{label} </span>
</span>
{docs ? (
<span className="daisy-label-text-alt">
<button
className="daisy-btn daisy-btn-ghost daisy-btn-sm daisy-btn-circle hover:daisy-btn-secondary"
onClick={() =>
setModal(
<ModalWrapper
flex="col"
justify="top lg:justify-center"
slideFrom="right"
keepOpenOnClick
>
<div className="markdown max-w-prose">{docs}</div>
</ModalWrapper>
)
}
>
<DocsIcon />
</button>
</span>
) : null}
</>
) )
const bottomLabelChildren = ( const bottomLabelChildren = (
<> <>
@ -149,7 +121,6 @@ export const NumberInput = ({
current, // The current value current, // The current value
original, // The original value original, // The original value
placeholder, // The placeholder text placeholder, // The placeholder text
docs = false, // Docs to load, if any
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
labelBL = false, // Bottom-Left label labelBL = false, // Bottom-Left label
labelBR = false, // Bottom-Right label labelBR = false, // Bottom-Right label
@ -157,7 +128,7 @@ export const NumberInput = ({
min = 220, min = 220,
step = 1, step = 1,
}) => ( }) => (
<FormControl {...{ label, labelBL, labelBR, docs }} forId={id}> <FormControl {...{ label, labelBL, labelBR }} forId={id}>
<input <input
id={id} id={id}
type="text" type="text"
@ -183,12 +154,11 @@ export const StringInput = ({
current, // The current value current, // The current value
original, // The original value original, // The original value
placeholder, // The placeholder text placeholder, // The placeholder text
docs = false, // Docs to load, if any
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
labelBL = false, // Bottom-Left label labelBL = false, // Bottom-Left label
labelBR = false, // Bottom-Right label labelBR = false, // Bottom-Right label
}) => ( }) => (
<FormControl {...{ label, labelBL, labelBR, docs }} forId={id}> <FormControl {...{ label, labelBL, labelBR }} forId={id}>
<input <input
id={id} id={id}
type="text" type="text"
@ -220,7 +190,6 @@ export const MfaInput = ({
valid={(val) => val.length > 4} valid={(val) => val.length > 4}
{...{ update, current, id }} {...{ update, current, id }}
placeholder="MFA Code" placeholder="MFA Code"
docs={false}
/> />
) )
} }
@ -234,7 +203,6 @@ export const PasswordInput = ({
valid, // Method that should return whether the value is valid or not valid, // Method that should return whether the value is valid or not
current, // The current value current, // The current value
placeholder = '¯\\_(ツ)_/¯', // The placeholder text placeholder = '¯\\_(ツ)_/¯', // The placeholder text
docs = false, // Docs to load, if any
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
onKeyDown = false, // Optionall capture certain keys (like enter) onKeyDown = false, // Optionall capture certain keys (like enter)
}) => { }) => {
@ -245,7 +213,6 @@ export const PasswordInput = ({
return ( return (
<FormControl <FormControl
label={label} label={label}
docs={docs}
forId={id} forId={id}
labelBR={ labelBR={
<button <button
@ -281,12 +248,11 @@ export const EmailInput = ({
current, // The current value current, // The current value
original, // The original value original, // The original value
placeholder, // The placeholder text placeholder, // The placeholder text
docs = false, // Docs to load, if any
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
labelBL = false, // Bottom-Left label labelBL = false, // Bottom-Left label
labelBR = false, // Bottom-Right label labelBR = false, // Bottom-Right label
}) => ( }) => (
<FormControl {...{ label, docs, labelBL, labelBR }} forId={id}> <FormControl {...{ label, labelBL, labelBR }} forId={id}>
<input <input
id={id} id={id}
type="email" type="email"
@ -301,21 +267,20 @@ export const EmailInput = ({
) )
/* /*
* Dropdown for designs * Input for designs
*/ */
export const DesignDropdown = ({ export const DesignInput = ({
label, // Label to use label, // Label to use
update, // onChange handler update, // onChange handler
current, // The current value current, // The current value
docs = false, // Docs to load, if any
firstOption = null, // Any first option to add in addition to designs firstOption = null, // Any first option to add in addition to designs
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
}) => { }) => {
return ( return (
<FormControl label={label} docs={docs} forId={id}> <FormControl label={label} forId={id}>
<select <select
id={id} id={id}
className="select select-bordered w-full" className="daisy-select daisy-select-bordered w-full"
onChange={(evt) => update(evt.target.value)} onChange={(evt) => update(evt.target.value)}
value={current} value={current}
> >
@ -338,7 +303,6 @@ export const ImageInput = ({
update, // The onChange handler update, // The onChange handler
current, // The current value current, // The current value
original, // The original value original, // The original value
docs = false, // Docs to load, if any
active = false, // Whether or not to upload images active = false, // Whether or not to upload images
imgType = 'showcase', // The image type imgType = 'showcase', // The image type
imgSubid, // The image sub-id imgSubid, // The image sub-id
@ -383,7 +347,7 @@ export const ImageInput = ({
if (current) if (current)
return ( return (
<FormControl label={label} docs={docs}> <FormControl label={label}>
<div <div
className="bg-base-100 w-full h-36 mb-2 mx-auto flex flex-col items-center text-center justify-center" className="bg-base-100 w-full h-36 mb-2 mx-auto flex flex-col items-center text-center justify-center"
style={{ style={{
@ -396,7 +360,7 @@ export const ImageInput = ({
}} }}
> >
<button <button
className="btn btn-neutral btn-circle opacity-50 hover:opacity-100" className="daisy-btn daisy-btn-neutral daisy-btn-circle opacity-50 hover:opacity-100"
onClick={() => update(original)} onClick={() => update(original)}
> >
<ResetIcon /> <ResetIcon />
@ -406,7 +370,7 @@ export const ImageInput = ({
) )
return ( return (
<FormControl label={label} docs={docs} forId={id}> <FormControl label={label} forId={id}>
<div <div
{...getRootProps()} {...getRootProps()}
className={` className={`
@ -417,7 +381,7 @@ export const ImageInput = ({
<input {...getInputProps()} /> <input {...getInputProps()} />
<p className="hidden lg:block p-0 m-0">Drag and drop and image here</p> <p className="hidden lg:block p-0 m-0">Drag and drop and image here</p>
<p className="hidden lg:block p-0 my-2">or</p> <p className="hidden lg:block p-0 my-2">or</p>
<button className={`btn btn-secondary btn-outline mt-4 px-8`}> <button className={`daisy-btn daisy-btn-secondary daisy-btn-outline mt-4 px-8`}>
Select an image to use Select an image to use
</button> </button>
</div> </div>
@ -433,7 +397,7 @@ export const ImageInput = ({
/> />
{active && ( {active && (
<button <button
className="btn btn-secondary ml-2 capitalize" className="daisy-btn daisy-btn-secondary ml-2 capitalize"
disabled={!url || url.length < 1} disabled={!url || url.length < 1}
onClick={() => upload(url, true)} onClick={() => upload(url, true)}
> >
@ -463,9 +427,8 @@ export const ListInput = ({
label, // The label label, // The label
list, // The list of items to present { val, label, desc } list, // The list of items to present { val, label, desc }
current, // The (value of the) current item current, // The (value of the) current item
docs = false, // Docs to load, if any
}) => ( }) => (
<FormControl label={label} docs={docs}> <FormControl label={label}>
{list.map((item, i) => ( {list.map((item, i) => (
<ButtonFrame key={i} active={item.val === current} onClick={() => update(item.val)}> <ButtonFrame key={i} active={item.val === current} onClick={() => update(item.val)}>
<div className="w-full flex flex-col gap-2"> <div className="w-full flex flex-col gap-2">
@ -489,15 +452,18 @@ export const MarkdownInput = ({
current, // The current value (markdown) current, // The current value (markdown)
update, // The onChange handler update, // The onChange handler
placeholder, // The placeholder content placeholder, // The placeholder content
docs = false, // Docs to load, if any
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
labelBL = false, // Bottom-Left label labelBL = false, // Bottom-Left label
labelBR = false, // Bottom-Right label labelBR = false, // Bottom-Right label
}) => ( }) => (
<FormControl {...{ label, labelBL, labelBR, docs }} forId={id}> <FormControl
{...{ label, labelBR }}
forId={id}
labelBL={labelBL ? labelBL : 'This field supports markdown'}
>
<Tabs tabs={['edit', 'preview']}> <Tabs tabs={['edit', 'preview']}>
<Tab key="edit"> <Tab key="edit">
<div className="flex flex-row items-center mt-4"> <div className="flex flex-row items-center">
<textarea <textarea
id={id} id={id}
rows="5" rows="5"
@ -509,8 +475,8 @@ export const MarkdownInput = ({
</div> </div>
</Tab> </Tab>
<Tab key="preview"> <Tab key="preview">
<div className="flex flex-row items-center mt-4"> <div className="flex flex-row items-center">
<Mdx md={current} /> <Markdown>{current}</Markdown>
</div> </div>
</Tab> </Tab>
</Tabs> </Tabs>
@ -523,7 +489,6 @@ export const MeasieInput = ({
original, // The original value original, // The original value
update, // The onChange handler update, // The onChange handler
placeholder, // The placeholder content placeholder, // The placeholder content
docs = false, // Docs to load, if any
id = '', // An id to tie the input to the label id = '', // An id to tie the input to the label
}) => { }) => {
const isDegree = isDegreeMeasurement(m) const isDegree = isDegreeMeasurement(m)
@ -553,14 +518,14 @@ export const MeasieInput = ({
if (!m) return null if (!m) return null
// Various visual indicators for validating the input // Various visual indicators for validating the input
let inputClasses = 'input-secondary' let inputClasses = 'daisy-input-secondary'
let bottomLeftLabel = null let bottomLeftLabel = null
if (valid === true) { if (valid === true) {
inputClasses = 'input-success' inputClasses = 'daisy-input-success'
const val = `${validatedVal}${isDegree ? '°' : imperial ? '"' : 'cm'}` const val = `${validatedVal}${isDegree ? '°' : imperial ? '"' : 'cm'}`
bottomLeftLabel = <span className="font-medium text-base text-success -mt-2 block">{val}</span> bottomLeftLabel = <span className="font-medium text-base text-success -mt-2 block">{val}</span>
} else if (valid === false) { } else if (valid === false) {
inputClasses = 'input-error' inputClasses = 'daisy-input-error'
bottomLeftLabel = ( bottomLeftLabel = (
<span className="font-medium text-error text-base -mt-2 block">¯\_()_/¯</span> <span className="font-medium text-error text-base -mt-2 block">¯\_()_/¯</span>
) )
@ -574,12 +539,7 @@ export const MeasieInput = ({
* See: https://github.com/facebook/react/issues/16554 * See: https://github.com/facebook/react/issues/16554
*/ */
return ( return (
<FormControl <FormControl label={m + (isDegree ? ' (°)' : '')} forId={id} labelBL={bottomLeftLabel}>
label={t(m) + (isDegree ? ' (°)' : '')}
docs={docs}
forId={id}
labelBL={bottomLeftLabel}
>
<input <input
id={id} id={id}
type="text" type="text"
@ -588,7 +548,7 @@ export const MeasieInput = ({
placeholder={placeholder} placeholder={placeholder}
value={localVal} value={localVal}
onChange={(evt) => localUpdate(evt.target.value)} onChange={(evt) => localUpdate(evt.target.value)}
className={`input w-full input-bordered ${inputClasses}`} className={`daisy-input w-full daisy-input-bordered ${inputClasses}`}
/> />
</FormControl> </FormControl>
) )
@ -628,7 +588,7 @@ export const FileInput = ({
<FormControl label={label} isValid={valid(current)}> <FormControl label={label} isValid={valid(current)}>
<div className="bg-base-100 w-full h-36 mb-2 mx-auto flex flex-col items-center text-center justify-center"> <div className="bg-base-100 w-full h-36 mb-2 mx-auto flex flex-col items-center text-center justify-center">
<button <button
className="btn btn-neutral btn-circle opacity-50 hover:opacity-100" className="daisy-btn daisy-btn-neutral daisy-btn-circle opacity-50 hover:opacity-100"
onClick={() => update(original)} onClick={() => update(original)}
> >
<ResetIcon /> <ResetIcon />
@ -651,7 +611,9 @@ export const FileInput = ({
> >
<input {...getInputProps()} /> <input {...getInputProps()} />
<p className="hidden lg:block p-0 m-0">Drag and drop your file here</p> <p className="hidden lg:block p-0 m-0">Drag and drop your file here</p>
<button className={`btn btn-secondary btn-outline mt-4 px-8`}>Browse...</button> <button className={`daisy-btn daisy-btn-secondary daisy-btn-outline mt-4 px-8`}>
Browse...
</button>
</div> </div>
</FormControl> </FormControl>
) )

View file

@ -5,5 +5,7 @@ import hljs from 'highlight.js/lib/common'
export const Json = (props) => { export const Json = (props) => {
const code = props.js ? JSON.stringify(props.js, null, 2) : props.children const code = props.js ? JSON.stringify(props.js, null, 2) : props.children
return <Highlight language="json" raw={hljs.highlight(code, { language: 'json' }).value} /> return (
<Highlight language="json" raw={hljs.highlight(code, { language: 'json' }).value} copy={code} />
)
} }

View file

@ -29,8 +29,8 @@ export const ModalWrapper = ({
flex = 'row', flex = 'row',
justify = 'center', justify = 'center',
items = 'center', items = 'center',
bg = 'base-100 lg:bg-base-300', bg = 'neutral lg:neutral',
bgOpacity = '100 lg:bg-opacity-95', bgOpacity = '100 lg:bg-opacity-70',
bare = false, bare = false,
keepOpenOnClick = false, keepOpenOnClick = false,
slideFrom = 'left', slideFrom = 'left',
@ -68,9 +68,10 @@ export const ModalWrapper = ({
<div <div
className={`fixed top-0 left-0 m-0 p-0 shadow w-full h-screen className={`fixed top-0 left-0 m-0 p-0 shadow w-full h-screen
transform-all duration-150 ${animation} transform-all duration-150 ${animation}
bg-${bg} bg-opacity-${bgOpacity} z-40 hover:cursor-pointer bg-${bg} bg-opacity-${bgOpacity} hover:cursor-pointer
flex flex-${flex} justify-${justify} items-${items} lg:p-12`} flex flex-${flex} justify-${justify} items-${items} lg:p-12 backdrop-blur-md`}
onClick={close} onClick={close}
style={{ zIndex: 250 }}
> >
{bare ? ( {bare ? (
children children

View file

@ -1,8 +1,104 @@
import React from 'react' import React, { useState } from 'react'
import { mergeProps } from './utils.mjs' import { CloseIcon } from '@freesewing/react/components/Icon'
import { Popout as SwizzledPopout } from './editor/swizzle/components/popout.mjs'
import { CloseIcon } from './editor/swizzle/components/icons.mjs'
const t = (id) => id const colors = {
comment: 'secondary',
error: 'error',
fixme: 'warning',
link: 'secondary',
none: '',
note: 'primary',
related: 'info',
tip: 'accent',
tldr: 'info',
warning: 'error',
}
export const Popout = (props) => <SwizzledPopout {...mergeProps(props, { CloseIcon }, { t })} /> /**
* This popout component is a way to make some content stand out
*
* @param {object} props - All React props
* @param {object} props.comment - Set this to make it a comment popout
* @param {object} props.error - Set this to make it a error popout
* @param {object} props.fixme - Set this to make it a fixme popout
* @param {object} props.link - Set this to make it a link popout
* @param {object} props.note - Set this to make it a note popout
* @param {object} props.related - Set this to make it a related popout
* @param {object} props.tip - Set this to make it a tip popout
* @param {object} props.tldr - Set this to make it a tldr popout
* @param {object} props.warning - Set this to make it a warning popout
* @param {string} props.title - The popout title
* @param {string} noP - Do not wrap the content in a p tag
*/
export const Popout = (props) => {
// Make this hideable/dismissable
const [hide, setHide] = useState(false)
if (hide) return null
let type = 'none'
for (const c in colors) {
if (props[c]) type = c
}
const color = colors[type]
const { className = '' } = props
return props.compact ? (
<div
className={`relative ${
props.dense ? 'my-1' : 'my-8'
} bg-${color} bg-opacity-5 -ml-4 -mr-4 sm:ml-0 sm:mr-0 ${className}`}
>
<div
className={`
border-y-4 sm:border-0 sm:border-l-4 px-4
shadow text-base border-${color}
flex flex-row items-center
`}
>
<div className={`font-bold uppercase text-${color}`}>
{props.title || (
<>
<span>{type.toUpperCase()}</span>
<span className="px-3">|</span>
</>
)}
</div>
<div className="popout-content">{props.noP ? props.children : <p>{props.children}</p>}</div>
</div>
</div>
) : (
<div
className={`relative my-8 bg-${color} bg-opacity-5 -ml-4 -mr-4 sm:ml-0 sm:mr-0 ${className}`}
>
<div
className={`
border-y-4 sm:border-0 sm:border-l-4 px-6 sm:px-8 py-4 sm:py-2
shadow text-base border-${color}
`}
>
<div className={`font-bold flex flex-row gap-1 items-end justify-between`}>
<div>
<span className={`font-bold uppercase text-${color}`}>
{type === 'tldr' ? 'TL;DR' : type.toUpperCase()}
</span>
<span className={`font-normal text-base text-${color}`}>
{type === 'comment' && (
<>
{' '}
by <b>{props.by}</b>
</>
)}
</span>
</div>
{props.hideable && (
<button onClick={() => setHide(true)} className="hover:text-secondary" title="Close">
<CloseIcon />
</button>
)}
</div>
<div className="py-1 first:mt-0 popout-content">{props.children}</div>
{type === 'comment' && <div className={`font-bold italic text-${color}`}>{props.by}</div>}
</div>
</div>
)
}

View file

@ -31,16 +31,17 @@ export const Tabs = ({ tabs = '', active = 0, children, withModal = false }) =>
) )
return ( return (
<div className="my-4"> <div className="">
<div className="tabs"> <div className="daisy-tabs daisy-tabs-bordered" role="tablist">
{tablist.map((title, tabId) => { {tablist.map((title, tabId) => {
const btnClasses = `text-lg font-bold capitalize tab h-auto tab-bordered grow py-2 ${ const btnClasses = `text-lg font-bold capitalize daisy-tab h-auto daisy-tabs-bordered grow py-1 ${
activeTab === tabId ? 'tab-active' : '' activeTab === tabId ? 'daisy-tab-active' : ''
}` }`
return withModal && activeTab === tabId ? ( return withModal && activeTab === tabId ? (
<button <button
key={tabId} key={tabId}
role="tab"
className={btnClasses} className={btnClasses}
onClick={() => onClick={() =>
setModal( setModal(

View file

@ -10,6 +10,11 @@ export const Yaml = (props) => {
else code = props.children else code = props.children
return ( return (
<Highlight {...props} language="yaml" raw={hljs.highlight(code, { language: 'yaml' }).value} /> <Highlight
{...props}
language="yaml"
raw={hljs.highlight(code, { language: 'yaml' }).value}
copy={code}
/>
) )
} }

View file

@ -47,7 +47,10 @@ const LoadingStatus = ({ loadingStatus }) => {
} }
return ( return (
<div className="fixed bottom-14 md:top-28 left-0 w-full z-50 md:px-4 md:mx-auto"> <div
className="fixed bottom-14 md:top-28 left-0 w-full z-50 md:px-4 md:mx-auto"
style={{ zIndex: 500 }}
>
<div <div
className={`w-full md:max-w-2xl m-auto bg-${color} flex flex-row items-center gap-4 p-4 px-4 ${fade} className={`w-full md:max-w-2xl m-auto bg-${color} flex flex-row items-center gap-4 p-4 px-4 ${fade}
transition-opacity delay-[${timeout * 1000 - 400}ms] duration-300 transition-opacity delay-[${timeout * 1000 - 400}ms] duration-300

View file

@ -3,4 +3,12 @@ title: Create a new measurement set
sidebar_position: 2 sidebar_position: 2
--- ---
FIXME import { DocusaurusDoc } from '@freesewing/react/components/Docusaurus'
import { RoleBlock } from '@freesewing/react/components/Role'
import { NewSet } from '@freesewing/react/components/Account'
<DocusaurusDoc>
<RoleBlock user>
<NewSet />
</RoleBlock>
</DocusaurusDoc>

161
sites/org/src/css/code.css Normal file
View file

@ -0,0 +1,161 @@
/* __SDEFILE__ - This file is a dependency for the stand-alone environment */
/*
* CSS file for highlightjs, with support for (DaisyUI) themes
*
* Do not edit this file, edit the theme configuration instead
*/
/* The div wrapping the code block */
div.hljs {
background: var(--code-background-color);
border-color: var(--code-border-color);
border-width: var(--code-border-width);
border-style: var(--code-border-style);
padding: 0;
color: var(--code-color);
border-radius: var(--code-border-radius);
}
/* The code block inside the wrapping div */
div.hljs > pre {
background: var(--code-bg);
color: var(--code-color);
padding: var(--code-inner-padding);
font-family: var(--code-font-family);
}
/*
* Fix for the tailwind typography plugin (aka prose) that
* adds padding to code that makes it look like there's a
* extra space on the first line
*/
div.hljs > pre > code {
padding: 0;
}
/*
* Higlight lines within a highlighted code block
*/
div.hljs > pre > code section {
margin: 0 -1rem;
padding: 0 1rem 0 calc(1rem - 4px);
border-left: 0.35rem solid transparent;
border-color: rgb(130, 203, 21);
background-color: var(--code-background-highlight-color);
}
div.hljs > pre > code section > div.code-section-inner {
display: inline-block;
padding: 0.25rem 0.25rem 0.25rem 0;
background-color: var(--code-background-highlight-color);
}
div.hljs > pre > code section.strikeout-lines {
border-color: rgb(255, 75, 57);
}
div.hljs > pre > code section > span.code-line-break:last-child {
display: none;
}
/*
* CSS for highlighted code: keyword
*/
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: var(--code-color-keyword);
font-weight: var(--code-font-weight-keyword);
}
/*
* CSS for highlighted code: entity
*/
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: var(--code-color-entity);
font-weight: var(--code-font-weight-entity);
}
/*
* CSS for highlighted code: constant
*/
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
color: var(--code-color-constant);
}
/*
* CSS for highlighted code: string
*/
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
color: var(--code-color-string);
font-style: var(--code-font-style-string);
}
/*
* CSS for highlighted code: variable
*/
.hljs-built_in,
.hljs-symbol {
color: var(--code-color-variable);
}
/*
* CSS for highlighted code: comment
*/
.hljs-comment,
.hljs-code,
.hljs-formula {
color: var(--code-color-comment);
}
/*
* CSS for highlighted code: tag
*/
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
color: var(--code-color-tag);
}
/*
* CSS for highlighted code: property
*/
.hljs-property {
color: var(--code-color-property);
font-weight: var(--code-font-weight-property);
}
/*
* CSS for highlighted code: other
*/
.hljs-addition,
.hljs-bullet,
.hljs-char.escape_,
.hljs-deletion,
.hljs-emphasis,
.hljs-link,
.hljs-params,
.hljs-punctuation,
.hljs-section,
.hljs-strong,
.hljs-subst,
.hljs-tag {
/* Currently not using these */
}

View file

@ -12,6 +12,11 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/*
* Add CSS for code
*/
@import './code.css';
@layer components { @layer components {
/* Applied styles for common HTML tags */ /* Applied styles for common HTML tags */
h1 { h1 {