Changelog
View as MarkdownWhat changed in each release of Fluid Primitives, newest first. Every release lists its breaking changes first, then the other changes under one heading per part of the library. The library follows semantic versioning, and the 1.0 betas may still change between releases.
Breaking changes
Permalink to heading "Breaking changes"ui:slotFilled: The ViewHelper is gone. Store the slot in a variable and test that:<f:variable name="note" value="{f:slot() -> f:spaceless()}" />, then<f:if condition="{note}">. This renders the slot once, where{ui:slotFilled()}followed by<f:slot />rendered it twice. Passnametof:slotfor a named slot. (#101)
Breaking changes
Permalink to heading "Breaking changes"- Core:
typo3fluid/fluidhas to be^5.2. (#90) - Core: A template mistake throws an exception of
Jramke\FluidPrimitives\Exceptioninstead of a bare\RuntimeExceptionor\InvalidArgumentException: a part outside its root is aMissingContextException, a missing prop aMissingPropException, a wrong value anInvalidPropValueException. Where you caught\InvalidArgumentExceptionaround a render, catchExceptionInterfaceinstead. (#90) - Field: The
validationModeof aFieldis strict:onBlurvalidates on blur only andonSubmiton submit only, where both used to revalidate on every change once an error showed. The behaviour of the oldonBlurdefault is the newautomode, which is the default now. SetvalidationMode="{f:constant(name: 'Jramke\FluidPrimitives\Enum\ValidationMode::Auto')}"where you relied on an error clearing as the user fixes it in a field that setsonBluroronSubmit.validationModeis no longer sent to the client unless a field sets one, so a field follows its form. (#95) - Form: Every form control submits its name while empty, so Extbase can clear the property: an unchecked
CheckboxorSwitch, aCheckboxGroupwith nothing checked, aSelectand aComboboxorDatePickerwithout a value send''through an empty-value input, asf:form.checkboxdoes. A name that ends with[]is sent without the brackets (tags, nottags[]), which Extbase maps to an empty array or ObjectStorage instead of['']. An emptyComboboxandDatePickerrender no value input any more, so ahiddenInputpart is there only while the control holds a value. The form values read an empty list as[], and an unchecked box as''. (#97) - Select, Combobox, Slider, DatePicker:
defaultValuetakes typed lists:string|string[]onSelectandCombobox(an integer such as a uid throws now, it never selected an item),float|integer|float[]|integer[]onSlider,DateTimeInterface|string|DateTimeInterface[]|string[]onDatePicker. (#97) - Slider: A range
Slidersubmits under thenameas you give it: end it with[](price[]) for PHP to receive both values. The client used to add the[]after hydration only. (#97)
General changes
Permalink to heading "General changes"- Input, Textarea:
ui:input.rootandui:textarea.roottakemaxLengthBehavior(Block,WarnorInvalid).WarnandInvalidleave off the nativemaxlength, so a paste is not cut off: the word count switches to the newwordCountOvertext ("Characters over the limit: 2"), and the root, the control and the count getdata-over-limit,data-at-limit(exactlymaxLengthcharacters) anddata-max-length-behavior, sowarncan be styled apart frominvalid.Invalidalso makes a surroundingFieldinvalid with thetooLongvalidity, soui:field.errorTextwith theValidityMatch::TooLongmatch works and a form does not submit. The count is in UTF-16 units like the nativemaxlength, in the browser and in the server render, so an emoji counts twice and the count is never lower thanmb_strlenor aVARCHAR(n)counts. The registry counter turnstext-destructiveover the limit and amber forWarn(runui:addagain to pick it up). (#96) - Field, Form:
data-empty-value="<name>"on an input tellsFieldandFormthat it stands for an empty value: a field whose only input is such an input has no value, and one whose name ends with[]has an empty list.extbase.post()andextbase.get()send an empty list as'', where they used to leave it out and Extbase kept the old value. (#97) - Every translation that a Zag.js machine wants as a function takes a text with
%placeholders%in a template, and the function itself in a client script.NumberInputtakesvalueText(%value%),SelecttakesitemAnnouncementanditemAnnouncementSelected(%label%,%value%, with English and German defaults in the language file), and thewordCountandwordCountOverofInputandTextareatake a function of{ count, max, over }.FileUploadandClipboardkeep the functions a client script passes (itemPreview,deleteFile,triggerLabel) where they used to drop them.fillPlaceholders()andresolveTranslations()do this for a component of your own. (#99) - Tabs, NumberInput: The registry
Tabstrigger and theNumberInputincrement and decrement triggers show a pointer cursor. TheNumberInputtriggers no longer setpointer-events: nonewhen disabled, so repeated clicks on one do not select the text next to it. Runui:addagain to pick it up. (#92) - Popover, Tooltip: The registry
PopoverandTooltipscale and fade out of their trigger on every placement and skip the scale when a visitor prefers reduced motion. They used to slide down and blur ontransition-all, which pointed the wrong way for content above its trigger. ThePopovertakes 250 ms instead of 150 ms. (#94)
destroyComponentsWithin()forgets the hydration data of the components it destroys, so a latermountAll()does not mount a removed row again. (#91)
- Everything the library throws implements
Jramke\FluidPrimitives\Exception\ExceptionInterface, so onecatchtakes it all. Fluid names the template file in the message of a ViewHelper that fails, and the exception of the library is thegetPrevious()of the one you catch. (#90) Typed::array()narrows a value to an array with a default, likeTyped::string()and the otherTypedmethods do for their types. (#100)
- A primitive can hand its
Fielda rule only it knows:setConstraint()on the field handle takes a function that returns a validity flag and a message, and aFieldAwareComponentreturns one fromgetFieldConstraint(). The field runs it on every validation. (#96)
FieldArray
Permalink to heading "FieldArray"- Removing a row of a
FieldArrayno longer breaks the components in the rows after it. ASelect,DatePickerorPopoverin a row that moved up kept reading and writing the ids it started with, so it stopped reacting, or opened the parts of a row you added later. A cloned row now keeps the root ids of its components until you remove it, and only thedata-valueof the row and the names of its fields change. (#91) - The fields of a
FieldArrayoutside aFormare renamed when you remove a row. Rows used to keeppeople[1]andpeople[2]after the first one was gone, and the next added row gotpeople[2]too. (#91)
ui:form.roottakesvalidationMode(auto,onBlur,onChangeoronSubmit) as the default for all its fields, and a field can still set its own.inputDebounceMsis a prop ofui:form.rootas well, so you no longer need your own entry file to change how long fields wait for a pause in typing. (#95)
NavigationMenu
Permalink to heading "NavigationMenu"- The registry
NavigationMenuviewport no longer needstw-animate-css: it opens and closes with the newanimate-viewport-inandanimate-viewport-oututilities. Copy their--animate-viewport-*theme entries from Styling before you update the viewport. Projects that added the component earlier keep their copy. (#98)
NumberInput
Permalink to heading "NumberInput"NumberInputwithformatOptionsno longer formats the value while you type. Typing1into a currency input used to turn it into$1.00right away, now the value is formatted on blur and enter. The changes the input makes itself (arrow keys, the step buttons, the mouse wheel, the scrubber, formatting on blur) now fire achangeevent instead of aninputevent, once the input shows the new text, so aFieldis told about them without the input reading its own old text back and a listener reads the new value. (#93)
Textarea
Permalink to heading "Textarea"- A
Textareadefault value that starts with a line break keeps it. The template wrote the value right behind the opening tag, where the HTML parser drops a first line break, so the value lost it and the count of the server-side render was one off. (#96)
Tocis a new primitive for a table of contents, built on the Zag.jstocmachine. It watches the headings of the page with anIntersectionObserverand marks the items of the visible ones withdata-activeand their links witharia-current="location". Pass the headings toui:toc.rootasitems, a list ofJramke\FluidPrimitives\Domain\Dto\TocItemobjects or of arrays, each with avalue(the id of the heading) and adepth, and render aui:toc.itemfor every one. The depth becomes the--depthCSS variable for indentation.scrollEltakes a CSS selector when the headings sit in a scroll container,ui:toc.titletakes alevel, andui:toc.indicatorfollows the active items through--top,--heightand--left. Nothing scrolls smoothly for a visitor who prefers reduced motion. (#100)
ui:emptyValueInput
Permalink to heading "ui:emptyValueInput"- Renders the input a control submits while it is empty. A context of your own takes
HasEmptyValueInputTraitfor the name of that input, and aFieldAwareComponentcallssyncEmptyValueInput()to disable it while the control holds a value. (#97)
Breaking changes
Permalink to heading "Breaking changes"ui:call: Escapes the text a method returns, like any other variable. Where you relied on HTML from it, usef:format.raw. (#87)- Core: A
ListCollectionsends the client only the value, label, disabled flag and group of an item, at the paths its keys name. Every other key stays on the server and a template reads it fromitem.original. (#87) ui:attributes: An attribute name with a space, quote,=,/,<,>or&throws, andhref,src,action,formaction,poster,xlink:href,citeanddataleave out ajavascript:orvbscript:URL.skipandonlyofui:attributesmatch names without regard to case. (#87)- Collapsible:
collapsedHeightandcollapsedWidthof aCollapsiblehave to be a CSS length or acalc(),min(),max()orclamp()expression, anything else throws. (#87)
General changes
Permalink to heading "General changes"- The package declares
"sideEffects": false. A component you import but never use, likeimport { Select } from 'fluid-primitives/select', no longer ends up in your bundle. (#88)
ui:addreads the registry of the installed version from the Composer package. Nothing is fetched from GitHub any more, and a checkout ofmaingets its own templates. (#87)ui:addrefuses a file name with a.or empty segment, does not write through a symlink out of the component folder, and reports a file it could not write instead of "Created". (#87)
- The hydration script keeps the whitespace of your data in production, so a label like
a = ( b )is no longer saved asa=(b). A site with a Content-Security-Policy gets a nonce for it. (#87) - A form field named
__proto__[x]is ignored, a quote in a root id cannot end the selector of a part, and a file name with$&is written as it is into ids and translations.ui:ref(rootId:)throws on a quote or backslash like a component'srootId. (#87) - A
ListCollectionitem with a number as its value or label is found by the client. Quoted values like'1'in an inline array no longer miss their item inSelectandCombobox. The items no longer render adata-itemattribute. (28e1504) asChildkeeps attribute values of the child element that contain$or\, liketitle="Cost $10". A value such as$0 onmouseover=...no longer breaks out of its attribute and adds an event handler. (#86)
- A
Selectshows the selected label and the placeholder as text. An option like<img src=x onerror=...>no longer runs as markup, andspreadPropsthrows on achildrenprop. (#87) - The item text and item indicator of a
Selectrender inside aui:template, so a stencil can hold an item the client clones, as inCombobox. (f4285d4)
ui:attributes
Permalink to heading "ui:attributes"- An attribute you forward with
attributes="{ui:attributes(asArray: true)}"or throughspreadPropsis escaped once.Tom & "Jerry"no longer arrives asTom & "Jerry". (#86)
Breaking changes
Permalink to heading "Breaking changes"- Form: Forms send through
extbase.post. TheonSubmitcallback of a form no longer receives aposthelper, callextbase.post(...)instead. The form element no longer rendersdata-field-name-prefixanddata-object-name. (#77) - Client:
extbase.get()andextbase.post()reject with anExtbaseValidationErrorwhen the server answers with a JSON422. Every other status still resolves as aResponse. (#77) - Core:
BaseContextis nowDefaultContext, markedfinaland@internal. A context that declaresgetGlobals()fails to load, andglobalsis a reserved prop name. (#73) - Form: A form rendered outside an Extbase request needs both
extensionNameandpluginName. Without them it throws instead of rendering fields without a prefix. (#70) - CLI:
ui:addwrites all of a component or nothing. When one of its files already exists the command fails with a non-zero exit code, add--forceto overwrite them all. (#71) ui:attributes: Never replaces an attribute the tag sets itself. Ahidden,styleorrolepassed by the caller no longer renders a second time. (#85)- Client: A component that overrides
init()has to returnthis. (#82) - Core: Two hydrated roots of the same component with the same
rootIdthrow. A root that is only server-rendered can repeat itsrootId. (#76)
General changes
Permalink to heading "General changes"- DatePicker, NumberInput:
DatePickerandNumberInputfall back to the global locale.NumberInputnow honours itslocaleprop. (#73)
- The component templates that
ui:addcopies load their entry file through a%componentPath%token. The command warns when theviteFluid namespace is not registered. (#72) - The registry keys of multi-word components are camelCase (
checkboxGroup).ui:add checkbox-groupkeeps working. (#74)
devWarn()andisDebug()print warnings in development only, for example whenmount()finds no hydration data. (#73)init()returns the instance, so a mount callback fits on one line.AsyncList.init()does too. (#82)
- A global context holds request-wide data for the server and the client. Read it in templates with
{context.globals.locale}and on the client withgetGlobal(). It ships thelocaleanddebugglobals, and a project can alias its ownGlobalContextInterface. (#73) - Clearer errors for
asChildwith several children, a part used outside its root, and a missingextensionNameorpluginNameon a form. (#69, #70) ListCollectionaccepts plain objects as items with any combination ofitemToValueKey,itemToStringKeyandisItemDisabledKey. (#67)
ui:addreads the component registry of a prerelease from its own tag. (#66)
General changes
Permalink to heading "General changes"ui:addreads the component registry from the tag of the installed version, so the files you copy match your library version. The registry is served fromraw.githubusercontent.com. (#65)
First beta release of Fluid Primitives 1.0.
Install it with composer require jramke/fluid-primitives:^1.0@beta and npm install fluid-primitives@beta.