1
0
Fork 0

chore(chared): Updated flag UI and api

This commit is contained in:
Joost De Cock 2023-09-09 15:31:53 +02:00
parent 893490d42a
commit f5edf74936
13 changed files with 158 additions and 112 deletions

View file

@ -6,8 +6,10 @@
"neckBinding": "Neck opening knit binding" "neckBinding": "Neck opening knit binding"
}, },
"s": { "s": {
"cutArmBinding": "Not shown: the **Arm opening knit binding**, two strips of fabric {{{ width }}} wide and {{{ length }}} long <small>(this includes seam allowance)</small>", "cutArmBinding.t": "The arm opening knit binding is not currently shown",
"cutNeckBinding": "Not shown: the **Neck opening knit binding**, a strip of fabric {{{ width }}} wide and {{{ length }}} long <small>(this includes seam allowance)</small>", "cutArmBinding.d": "The **Arm opening knit binding** are two strips of fabric {{{ width }}} wide and {{{ length }}} long (this includes seam allowance). They are not shown because the **expand** core setting is currently disabled. Enable it to show this pattern part.",
"cutNeckBinding.t": "The neck opening knit binding is not currently shown",
"cutNeckBinding.d": "The **Neck opening knit binding** is a strip of fabric {{{ width }}} wide and {{{ length }}} long (this includes seam allowance). It is not shown because the **expand** core setting is currently disabled. Enable it to show this pattern part.",
"cutOneStripToFinishTheNeckOpening": "Cut one strip to finish the neck opening", "cutOneStripToFinishTheNeckOpening": "Cut one strip to finish the neck opening",
"cutTwoStripsToFinishTheArmholes": "Cut two strips to finish the armholes", "cutTwoStripsToFinishTheArmholes": "Cut two strips to finish the armholes",
"length": "Length", "length": "Length",

View file

@ -23,7 +23,8 @@ export const armBinding = {
if (!expand) { if (!expand) {
// Expand is on, do not draw the part but flag this to the user // Expand is on, do not draw the part but flag this to the user
store.flag.note({ store.flag.note({
msg: `aaron:cutArmBinding`, title: `aaron:cutArmBinding.t`,
desc: `aaron:cutArmBinding.d`,
replace: { replace: {
width: units(w), width: units(w),
length: units(l), length: units(l),

View file

@ -23,7 +23,8 @@ export const neckBinding = {
if (!expand) { if (!expand) {
// Expand is on, do not draw the part but flag this to the user // Expand is on, do not draw the part but flag this to the user
store.flag.note({ store.flag.note({
msg: `aaron:cutNeckBinding`, title: `aaron:cutNeckBinding.t`,
desc: `aaron:cutNeckBinding.d`,
replace: { replace: {
width: units(w), width: units(w),
length: units(l), length: units(l),

View file

@ -8,8 +8,10 @@
}, },
"s": { "s": {
"attachStrap": "Attach strap", "attachStrap": "Attach strap",
"cutPocket": "Not shown: the **Pocket**, a rectangle of {{{ width }}} wide and {{{ length }}} long <small>(this includes seam allowance)</small>", "cutPocket.t": "The pocket is not currently shown",
"cutStrap": "Not shown: the **Straps**, two rectangles of {{{ width }}} wide and {{{ length }}} long <small>(this includes seam allowance)</small>", "cutPocket.d": "The **Pocket** is a rectangular piece of fabric {{{ width }}} wide and {{{ length }}} long (this includes seam allowance). It is not shown because the **expand** core setting is currently disabled. Enable it to show this pattern part.",
"cutStrap.t": "The straps are not currently shown",
"cutStrap.d": "The **Straps** are two strips of fabric {{{ width }}} wide and {{{ length }}} long (this includes seam allowance)</small>. They are not shown because the **expand** core setting is currently disabled. Enable it to show this pattern part.",
"foldHere": "Fold here" "foldHere": "Fold here"
}, },
"o": { "o": {

View file

@ -22,7 +22,8 @@ export const pocket = {
if (!expand) { if (!expand) {
// Expand is on, do not draw the part but flag this to the user // Expand is on, do not draw the part but flag this to the user
store.flag.note({ store.flag.note({
msg: `albert:cutPocket`, title: `albert:cutPocket.t`,
desc: `albert:cutPocket.d`,
replace: { replace: {
width: units(pocketSize * 2 + 2 * sa), width: units(pocketSize * 2 + 2 * sa),
length: units(pocketSize + 2 * sa + store.get('strapWidth')), length: units(pocketSize + 2 * sa + store.get('strapWidth')),

View file

@ -41,10 +41,11 @@ export const strap = {
if (!expand) { if (!expand) {
// Expand is on, do not draw the part but flag this to the user // Expand is on, do not draw the part but flag this to the user
store.flag.note({ store.flag.note({
msg: `albert:cutStrap`, title: `albert:cutStrap.t`,
desc: `albert:cutStrap.d`,
replace: { replace: {
width: units(store.get('strapWidth') + 2 * sa), width: units(strapWidth + 2 * sa),
length: units(store.get('strapLength') + store.get('strapWidth') * 2 + 2 * sa), length: units(strapLength + strapWidth * 2 + 2 * sa),
}, },
suggest: { suggest: {
text: 'flag:show', text: 'flag:show',

View file

@ -34,13 +34,13 @@ function flag(type, store, data) {
type = data.type type = data.type
} }
if (!data.id && !data.msg) { if (!data.id && !data.title) {
store.log.warning(`store.flag.${type} called without an id or msg property`) store.log.warn(`store.flag.${type} called without an id or title property`)
console.log(data) console.log(data)
return return
} }
store.set([...storeRoot, type, data.id ? data.id : data.msg], data) store.set([...storeRoot, type, data.id ? data.id : data.title], data)
} }
/* /*
@ -53,7 +53,7 @@ function flag(type, store, data) {
function unflag(type, store, id) { function unflag(type, store, id) {
if (type === 'preset' && presets[id]) { if (type === 'preset' && presets[id]) {
type = presets[id].type type = presets[id].type
id = presets[id].id || presets[id].msg id = presets[id].id || presets[id].title
} }
store.unset([...storeRoot, type, id]) store.unset([...storeRoot, type, id])
} }
@ -64,7 +64,8 @@ function unflag(type, store, id) {
const presets = { const presets = {
expand: { expand: {
type: 'tip', type: 'tip',
msg: 'flag:expandIsOff', title: 'flag:expandIsOff.t',
desc: 'flag:expandIsOff.d',
suggest: { suggest: {
text: 'flag:enable', text: 'flag:enable',
icon: 'expand', icon: 'expand',

View file

@ -26,22 +26,25 @@ const BaseAccordion = ({
}) => { }) => {
const [active, setActive] = useState() const [active, setActive] = useState()
console.log(items)
return ( return (
<nav> <nav>
{items.map((item, i) => {items
active === i ? ( .filter((item) => item[0])
<div key={i} {...propsGetter(active, i)}> .map((item, i) =>
<button onClick={setActive} className="w-full"> active === i ? (
<div key={i} {...propsGetter(active, i)}>
<button onClick={setActive} className="w-full">
{item[0]}
</button>
{item[1]}
</div>
) : (
<button key={i} {...getProps(active, i)} onClick={() => setActive(i)}>
{item[0]} {item[0]}
</button> </button>
{item[1]} )
</div> )}
) : (
<button key={i} {...getProps(active, i)} onClick={() => setActive(i)}>
{item[0]}
</button>
)
)}
</nav> </nav>
) )
} }

View file

@ -318,6 +318,12 @@ export const FingerprintIcon = (props) => (
</IconWrapper> </IconWrapper>
) )
export const FlagIcon = (props) => (
<IconWrapper {...props}>
<path d="M3 3v1.5M3 21v-6m0 0l2.77-.693a9 9 0 016.208.682l.108.054a9 9 0 006.086.71l3.114-.732a48.524 48.524 0 01-.005-10.499l-3.11.732a9 9 0 01-6.085-.711l-.108-.054a9 9 0 00-6.208-.682L3 4.5M3 15V4.5" />
</IconWrapper>
)
export const FreeSewingIcon = (props) => ( export const FreeSewingIcon = (props) => (
<IconWrapper {...props} stroke={0} fill> <IconWrapper {...props} stroke={0} fill>
<path d={logoPath} /> <path d={logoPath} />

View file

@ -49,7 +49,6 @@ export const DraftView = ({
pattern: output, pattern: output,
setSettings, setSettings,
Header: DraftHeader, Header: DraftHeader,
flags: pattern.setStores?.[0]?.plugins?.['plugin-annotations']?.flags,
menu: ( menu: (
<DraftMenu <DraftMenu
{...{ {...{
@ -66,6 +65,7 @@ export const DraftView = ({
renderProps, renderProps,
view, view,
setView, setView,
flags: pattern.setStores?.[0]?.plugins?.['plugin-annotations']?.flags,
}} }}
/> />
), ),

View file

@ -9,8 +9,12 @@ import {
import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs' import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { nsMerge } from 'shared/utils.mjs' import { nsMerge } from 'shared/utils.mjs'
import { SettingsIcon, OptionsIcon, DesktopIcon } from 'shared/components/icons.mjs' import { SettingsIcon, OptionsIcon, DesktopIcon, FlagIcon } from 'shared/components/icons.mjs'
import { Accordion } from 'shared/components/accordion.mjs' import { Accordion } from 'shared/components/accordion.mjs'
import {
FlagsAccordionTitle,
FlagsAccordionEntries,
} from 'shared/components/workbench/views/flags.mjs'
export const ns = nsMerge(coreMenuNs, designMenuNs, uiNs) export const ns = nsMerge(coreMenuNs, designMenuNs, uiNs)
@ -26,6 +30,7 @@ export const DraftMenu = ({
DynamicDocs, DynamicDocs,
view, view,
setView, setView,
flags = false,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const control = account.control const control = account.control
@ -61,18 +66,24 @@ export const DraftMenu = ({
}, },
] ]
return ( const items = []
<Accordion if (flags)
items={sections.map((section) => [ items.push([
<> <FlagsAccordionTitle flags={flags} />,
<h5 className="flex flex-row gap-2 items-center justify-between w-full"> <FlagsAccordionEntries {...{ update, control, flags }} />,
<span>{t(`${section.ns}:${section.name}.t`)}</span> ])
{section.icon} items.push(
</h5> ...sections.map((section) => [
<p className="text-left">{t(`${section.ns}:${section.name}.d`)}</p> <>
</>, <h5 className="flex flex-row gap-2 items-center justify-between w-full">
section.menu, <span>{t(`${section.ns}:${section.name}.t`)}</span>
])} {section.icon}
/> </h5>
<p className="text-left">{t(`${section.ns}:${section.name}.d`)}</p>
</>,
section.menu,
])
) )
return <Accordion items={items} />
} }

View file

@ -13,16 +13,10 @@ import {
ErrorIcon, ErrorIcon,
WrenchIcon as FixmeIcon, WrenchIcon as FixmeIcon,
ExpandIcon, ExpandIcon,
FlagIcon,
} from 'shared/components/icons.mjs' } from 'shared/components/icons.mjs'
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import { SubAccordion } from 'shared/components/accordion.mjs'
const flagColors = {
note: 'primary',
tip: 'accent',
warn: 'error',
error: 'error',
fixme: 'warning',
}
const flagIcons = { const flagIcons = {
note: ChatIcon, note: ChatIcon,
@ -34,39 +28,14 @@ const flagIcons = {
expand: ExpandIcon, expand: ExpandIcon,
} }
export const Flags = ({ flags, update }) => { export const Flag = ({ type, data, t, handleUpdate }) => {
const handleUpdate = (config) => {
if (config.settings) update.settings(...config.settings)
if (config.ui) update.ui(...config.settings)
}
return (
<div className="p-4 flex flex-row overflow-x-auto w-full gap-4 max-w-screen">
{flagTypes.map((type) =>
flags[type]
? Object.values(flags[type]).map((flag) => (
<Flag type={type} data={flag} handleUpdate={handleUpdate} key={flag.id} />
))
: null
)}
</div>
)
}
export const Flag = ({ type, data, handleUpdate }) => {
const { t } = useTranslation(nsMerge('flag', data.ns, data.msg.split(':').shift()))
const [hide, setHide] = useState(false)
if (hide || !data.msg) return null
const color = flagColors[type]
const Icon = flagIcons[type] const Icon = flagIcons[type]
const BtnIcon = data.suggest?.icon ? flagIcons[data.suggest.icon] : false const BtnIcon = data.suggest?.icon ? flagIcons[data.suggest.icon] : false
const button = const button =
data.suggest?.text && data.suggest?.update ? ( data.suggest?.text && data.suggest?.update ? (
<button <button
className={`btn btn-sm sm:btn-normal btn-neutral btn-outline grow flex flex-row items-center justify-between sm:grow-0 shrink-0 z-10 ${ className={`btn btn-secondary btn-outline flex flex-row items-center ${
BtnIcon ? 'gap-6' : '' BtnIcon ? 'gap-6' : ''
}`} }`}
onClick={() => handleUpdate(data.suggest.update)} onClick={() => handleUpdate(data.suggest.update)}
@ -76,43 +45,87 @@ export const Flag = ({ type, data, handleUpdate }) => {
</button> </button>
) : null ) : null
const msg = data.replace const desc = data.replace ? mustache.render(t(data.desc), data.replace) : t(data.desc)
? mustache.render(t(data.msg), { ...data.replace, '&quot;': '"' })
: t(data.msg)
return ( return (
<div className="w-4/5 max-w-md shrink-0"> <div className="flex flex-col gap-2 items-start">
<div className={`relative bg-${color} bg-opacity-10`}> <div className="first:mt-0 grow md flag">
<div className="p-3 rounded-lg shadow text-base"> <Markdown children={desc} />
<div className="flex flex-row flex-wrap sm:flex-nowrap gap-2 items-start z-10">
<div className="first:mt-0 popout-content grow z-10 md flag">
<Markdown>{msg}</Markdown>
</div>
{button ? (
<div className="flex flex-row justify-between sm:flex-col gap-2 shrink-0 z-10 w-full sm:w-auto">
{button}
<button
className="w-1/2 sm:w-full btn btn-ghost btn-sm z-10 flex flex-row items-center justify-between w-full"
onClick={() => setHide(true)}
>
{t('flag:dismiss')}
<Icon className="w-5 h-6 sm:w-6 h-6" />
</button>
</div>
) : (
<div className="flex flex-col gap-2">
<button
className="btn btn-sm btn-circle btn-ghost"
onClick={() => setHide(true)}
title={t('flag:hide')}
>
<Icon className="w-5 h-5 sm:w-6 h-6" />
</button>
</div>
)}
</div>
</div>
</div> </div>
{button ? <div className="mt-2 w-full flex flex-row justify-end">{button}</div> : null}
</div> </div>
) )
} }
const flattenFlags = (flags) => {
const all = {}
const ns = ['flag']
for (const type of flagTypes) {
let i = 0
if (flags[type]) {
for (const [key, flag] of Object.entries(flags[type])) {
i++
all[`${type}-${i}`] = { ...flag, type }
if (flag.ns) ns.push(flag.ns)
if (flag.title.includes(':')) ns.push(flag.title.split(':').shift())
if (flag.desc.includes(':')) ns.push(flag.desc.split(':').shift())
}
}
}
return [all, ns]
}
export const FlagsAccordionTitle = ({ flags }) => {
const { t } = useTranslation(['flag'])
const [flagList] = flattenFlags(flags)
if (Object.keys(flagList).length < 1) return null
return (
<>
<h5 className="flex flex-row gap-2 items-center justify-between w-full">
<span>
{t('flag:flagMenu.t')} ({Object.keys(flagList).length})
</span>
<FlagIcon className="w-8 h-8" />
</h5>
<p className="text-left">
{Object.keys(flagList).length > 1 ? t('flag:flagMenuMany.d') : t('flag:flagMenuOne.d')}
</p>
</>
)
}
export const FlagsAccordionEntries = ({ flags, update }) => {
const [flagList, ns] = flattenFlags(flags)
const { t } = useTranslation(nsMerge(ns))
const [all, setAll] = useState(flagList)
if (Object.keys(flagList).length < 1) return null
const handleUpdate = (config) => {
if (config.settings) update.settings(...config.settings)
if (config.ui) update.ui(...config.settings)
}
return (
<SubAccordion
items={Object.entries(all).map(([key, flag], i) => {
const Icon = flagIcons[flag.type]
const title = flag.replace ? mustache.render(t(flag.title), flag.replace) : t(flag.title)
return [
<div className="w-full flex flex-row gap2 justify-between" key={i}>
<div className="flex flex-row items-center gap-2">
<Icon />
<span className="font-medium">{title}</span>
</div>
<span className="uppercase font-bold">{flag.type}</span>
</div>,
<Flag key={key} t={t} type={flag.type} data={flag} handleUpdate={handleUpdate} />,
]
})}
/>
)
}

View file

@ -1,5 +1,9 @@
dismiss: Dismiss dismiss: Dismiss
expandIsOff: The **expand** core setting is disabled. Some parts are not fully drawn. Enable it to see them. expandIsOff.t: This design saves space (and trees) because expand is disabled
expandIsOff.d: Because the **expand** core setting is currently disabled, some parts are not fully drawn or not shown at all. Typically, these are simple rectangles that only take up space. To expand all pattern parts to their full size, enable the expand setting.
enable: Enable enable: Enable
flagMenu.t: Messages from the designer
flagMenuOne.d: The designer of this pattern has flagged something about your current draft that deserves your attention.
flagMenuMany.d: The designer of this pattern has flagged some things about your current draft that deserve your attention.
hide: Hide hide: Hide
show: Show show: Show