forked from qwikifiers/qwik-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into add_component_switch
- Loading branch information
Showing
42 changed files
with
886 additions
and
424 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@qwik-ui/headless': minor | ||
--- | ||
|
||
We are removing the existing popover animations shimming and instead wil now only support native popover animations. This is considered a breaking change but will be more reliable overall. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ node_modules | |
dist | ||
coverage | ||
.eslintrc.* | ||
vite.config.ts | ||
vite.config.ts | ||
packages/kit-headless/browsers/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ on: | |
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
name: Test NodeJS ${{ matrix.node_version }} | ||
|
||
strategy: | ||
matrix: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import * as fs from 'fs'; | ||
import { resolve } from 'path'; | ||
import { ViteDevServer } from 'vite'; | ||
export default function autoAPI() { | ||
return { | ||
name: 'watch-monorepo-changes', | ||
configureServer(server: ViteDevServer) { | ||
const watchPath = resolve(__dirname, '../../packages/kit-headless'); | ||
server.watcher.on('change', (file: string) => { | ||
if (file.startsWith(watchPath)) { | ||
loopOnAllChildFiles(file); | ||
} | ||
}); | ||
}, | ||
}; | ||
} | ||
// the object should have this general structure, arranged from parent to child | ||
// componentName:[subComponent,subcomponent,...] | ||
// subComponentName:[publicType,publicType,...] | ||
// publicType:[{ comment,prop,type },{ comment,prop,type },...] | ||
// THEY UPPER-MOST KEY IS ALWAYS USED AS A HEADING | ||
export type ComponentParts = Record<string, SubComponents>; | ||
type SubComponents = SubComponent[]; | ||
export type SubComponent = Record<string, PublicType[]>; | ||
export type PublicType = Record<string, ParsedProps[]>; | ||
type ParsedProps = { | ||
comment: string; | ||
prop: string; | ||
type: string; | ||
}; | ||
function parseSingleComponentFromDir(path: string, ref: SubComponents) { | ||
const component_name = /\/([\w-]*).tsx/.exec(path); | ||
if (component_name === null || component_name[1] === null) { | ||
// may need better behavior | ||
return; | ||
} | ||
const sourceCode = fs.readFileSync(path, 'utf-8'); | ||
const comments = extractPublicTypes(sourceCode); | ||
const parsed: PublicType[] = []; | ||
for (const comment of comments) { | ||
const api = extractComments(comment.string); | ||
const pair: PublicType = { [comment.label]: api }; | ||
parsed.push(pair); | ||
} | ||
const completeSubComponent: SubComponent = { [component_name[1]]: parsed }; | ||
ref.push(completeSubComponent); | ||
return ref; | ||
} | ||
|
||
function extractPublicTypes(strg: string) { | ||
const getPublicTypes = /type Public([A-Z][\w]*)*[\w\W]*?{([\w|\W]*?)}(;| &)/gm; | ||
const cms = []; | ||
let groups; | ||
while ((groups = getPublicTypes.exec(strg)) !== null) { | ||
const string = groups[2]; | ||
cms.push({ label: groups[1], string }); | ||
} | ||
return cms; | ||
} | ||
function extractComments(strg: string): ParsedProps[] { | ||
const magical_regex = | ||
/^\s*?\/[*]{2}\n?([\w|\W|]*?)\s*[*]{1,2}[/]\n[ ]*([\w|\W]*?): ([\w|\W]*?);?$/gm; | ||
|
||
const cms = []; | ||
let groups; | ||
|
||
while ((groups = magical_regex.exec(strg)) !== null) { | ||
const trimStart = /^ *|(\* *)/g; | ||
const comment = groups[1].replaceAll(trimStart, ''); | ||
const prop = groups[2]; | ||
const type = groups[3]; | ||
cms.push({ comment, prop, type }); | ||
} | ||
return cms; | ||
} | ||
function writeToDocs(fullPath: string, componentName: string, api: ComponentParts) { | ||
if (fullPath.includes('kit-headless')) { | ||
const relDocPath = `../website/src/routes//docs/headless/${componentName}`; | ||
const fullDocPath = resolve(__dirname, relDocPath); | ||
const dirPath = fullDocPath.concat('/auto-api'); | ||
|
||
if (!fs.existsSync(dirPath)) { | ||
fs.mkdirSync(dirPath); | ||
} | ||
const json = JSON.stringify(api, null, 2); | ||
const hacky = `export const api=${json}`; | ||
|
||
try { | ||
fs.writeFileSync(dirPath.concat('/api.ts'), hacky); | ||
console.log('auto-api: succesfully genereated new json!!! :)'); | ||
} catch (err) { | ||
return; | ||
} | ||
} | ||
} | ||
function loopOnAllChildFiles(filePath: string) { | ||
const childComponentRegex = /\/([\w-]*).tsx$/.exec(filePath); | ||
if (childComponentRegex === null) { | ||
return; | ||
} | ||
const parentDir = filePath.replace(childComponentRegex[0], ''); | ||
const componentRegex = /\/(\w*)$/.exec(parentDir); | ||
if (!fs.existsSync(parentDir) || componentRegex == null) { | ||
return; | ||
} | ||
const componentName = componentRegex[1]; | ||
const allParts: SubComponents = []; | ||
const store: ComponentParts = { [componentName]: allParts }; | ||
fs.readdirSync(parentDir).forEach((fileName) => { | ||
if (/tsx$/.test(fileName)) { | ||
const fullPath = parentDir + '/' + fileName; | ||
parseSingleComponentFromDir(fullPath, store[componentName]); | ||
} | ||
}); | ||
|
||
writeToDocs(filePath, componentName, store); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { component$ } from '@builder.io/qwik'; | ||
import { Note, NoteStatus } from '../note/note'; // Adjust the import path based on your structure | ||
|
||
export const TopLayerAnimationsCaveats = component$(() => { | ||
return ( | ||
<Note status={NoteStatus.Warning}> | ||
<strong>Important Caveats for Animating Discrete Properties</strong> | ||
|
||
<ul class="mt-4 list-disc bg-gradient-to-b pl-4"> | ||
<li> | ||
<strong> | ||
Animating <code>display</code> and <code>overlay</code>: | ||
</strong> | ||
<p> | ||
The <code>display</code> property must be included in the transitions list to | ||
ensure the element remains visible throughout the animation. The value flips | ||
from <code>none</code> to <code>block</code> at 0% of the animation, ensuring | ||
visibility for the entire duration. The | ||
<code>overlay</code> ensures the element stays in the top layer until the | ||
animation completes. | ||
</p> | ||
</li> | ||
<li> | ||
<strong> | ||
Using <code>transition-behavior: allow-discrete</code>: | ||
</strong> | ||
<p> | ||
This property is essential when animating discrete properties like{' '} | ||
<code>display</code> and <code>overlay</code>, which are not typically | ||
animatable. It ensures smooth transitions for these discrete properties. | ||
</p> | ||
</li> | ||
<li> | ||
<strong> | ||
Setting Starting Styles with <code>@starting-style</code>: | ||
</strong> | ||
<p> | ||
CSS transitions are only triggered when a property changes on a visible | ||
element. The | ||
<code>@starting-style</code> at-rule allows you to set initial styles (e.g.,{' '} | ||
<code>opacity</code> and | ||
<code>transform</code>) when the element first appears, ensuring that the | ||
animation behaves predictably. | ||
</p> | ||
</li> | ||
</ul> | ||
</Note> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { component$ } from '@builder.io/qwik'; | ||
import { Note, NoteStatus } from '../note/note'; // Adjust the import path based on your structure | ||
|
||
export const BrowserAnimationsCompatability = component$(() => { | ||
return ( | ||
<Note status={NoteStatus.Info}> | ||
<div class="flex flex-col gap-2"> | ||
<h4> | ||
<strong>Browser Compatability</strong> | ||
</h4> | ||
<p> | ||
<a href="https://caniuse.com/?search=popover%20API"> | ||
Browser versions that do not support the popover API natively | ||
</a>{' '} | ||
have known issues when trying to use animations or transitions. If you need to | ||
support legacy versions of browsers, please be sure to test this functionality | ||
independently. | ||
</p> | ||
</div> | ||
</Note> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { JSXOutput, component$, $, QRL, useTask$, useSignal } from '@builder.io/qwik'; | ||
import { APITable, type APITableProps } from './api-table'; | ||
|
||
//This is a workaround for not being able to export across packages due to nx rule: | ||
// https://nx.dev/features/enforce-module-boundaries#enforce-module-boundaries | ||
type ComponentParts = Record<string, SubComponents>; | ||
type SubComponents = SubComponent[]; | ||
type SubComponent = Record<string, PublicType[]>; | ||
type PublicType = Record<string, ParsedProps[]>; | ||
type ParsedProps = { | ||
comment: string; | ||
prop: string; | ||
type: string; | ||
}; | ||
type AutoAPIConfig = { | ||
topHeader?: QRL<(text: string) => JSXOutput>; | ||
subHeader?: QRL<(text: string) => JSXOutput>; | ||
props?: QRL<(text: string) => string>; | ||
}; | ||
|
||
type AnatomyTableProps = { | ||
api?: ComponentParts; | ||
config: AutoAPIConfig; | ||
}; | ||
|
||
type SubComponentProps = { | ||
subComponent: SubComponent; | ||
config: AutoAPIConfig; | ||
}; | ||
type ParsedCommentsProps = { | ||
parsedProps: PublicType; | ||
config: AutoAPIConfig; | ||
}; | ||
const currentHeader = $(() => { | ||
//cannot send h2 from here because current TOC can only read md | ||
return null; | ||
}); | ||
|
||
const currentSubHeader = $((text: string) => { | ||
let subHeader = text.replace(/(p|P)rops/, ''); | ||
const hasCapital = /[a-z][A-Z]/.exec(subHeader)?.index; | ||
if (hasCapital != undefined) { | ||
subHeader = | ||
subHeader.slice(0, hasCapital + 1) + '.' + subHeader.slice(hasCapital + 1); | ||
} | ||
return ( | ||
<> | ||
<h3 class="mb-6 mt-8 scroll-mt-20 text-xl font-semibold">{subHeader}</h3> | ||
</> | ||
); | ||
}); | ||
|
||
const removeQuestionMarkFromProp = $((text: string) => { | ||
return text.replace('?', ''); | ||
}); | ||
const defaultConfig: AutoAPIConfig = { | ||
topHeader: currentHeader, | ||
subHeader: currentSubHeader, | ||
props: removeQuestionMarkFromProp, | ||
}; | ||
export const AutoAPI = component$<AnatomyTableProps>( | ||
({ api, config = defaultConfig }) => { | ||
if (api === undefined) { | ||
return null; | ||
} | ||
const key = Object.keys(api)[0]; | ||
const topHeaderSig = useSignal<string | JSXOutput>(key); | ||
const subComponents = api[key].filter((e) => e[Object.keys(e)[0]].length > 0); | ||
useTask$(async () => { | ||
if (config.topHeader) { | ||
topHeaderSig.value = await config.topHeader(key as string); | ||
} | ||
}); | ||
return ( | ||
<> | ||
{topHeaderSig.value} | ||
{subComponents.map((e, index) => ( | ||
<SubComponent key={index} subComponent={e} config={config} /> | ||
))} | ||
</> | ||
); | ||
}, | ||
); | ||
|
||
const SubComponent = component$<SubComponentProps>(({ subComponent, config }) => { | ||
const subComponentKey = Object.keys(subComponent)[0]; | ||
const comments = subComponent[subComponentKey]; | ||
return ( | ||
<> | ||
{comments.map((e) => ( | ||
<> | ||
<ParsedComments parsedProps={e} config={config} /> | ||
</> | ||
))} | ||
</> | ||
); | ||
}); | ||
|
||
const ParsedComments = component$<ParsedCommentsProps>(({ parsedProps, config }) => { | ||
const key = Object.keys(parsedProps)[0]; | ||
const subHeaderSig = useSignal<string | JSXOutput>(key); | ||
useTask$(async () => { | ||
if (config.subHeader) { | ||
subHeaderSig.value = await config.subHeader(key as string); | ||
} | ||
}); | ||
const appliedPropsSig = useSignal<null | APITableProps>(null); | ||
useTask$(async () => { | ||
const translation: APITableProps = { | ||
propDescriptors: parsedProps[key].map((e) => { | ||
const isObject = e.type.includes('{'); | ||
const isUnion = e.type.includes('|'); | ||
const isPopup = isObject || isUnion; | ||
|
||
return { | ||
name: e.prop, | ||
type: isObject ? 'object' : isUnion ? 'union' : e.type, | ||
description: e.comment, | ||
info: (isPopup && e.type) || undefined, | ||
}; | ||
}), | ||
}; | ||
if (config.props) { | ||
for (const props of translation.propDescriptors) { | ||
props.name = await config.props(props.name); | ||
} | ||
} | ||
appliedPropsSig.value = translation; | ||
}); | ||
return ( | ||
<> | ||
{subHeaderSig.value} | ||
{appliedPropsSig.value?.propDescriptors && ( | ||
<APITable propDescriptors={appliedPropsSig.value?.propDescriptors} /> | ||
)} | ||
</> | ||
); | ||
}); |
Oops, something went wrong.