@cnukorg/cn-vue-component-library
v0.0.25
Published
Official ConnectNow component library used for ConnectNow applications on VueJS 3. Make sure to check out the "Required dependencies" section to see what also needs to be included in your project.
Downloads
9
Readme
ConnectNow Component Library for Vue
Official ConnectNow component library used for ConnectNow applications on VueJS 3. Make sure to check out the "Required dependencies" section to see what also needs to be included in your project.
Contributing
Make changes where required and merge into master, at which point the NPM package should be versioned and pushed
Required dependencies
See: peerDependencies in package.json for latest list. You must have these dependencies in your project for the component library to fully work.
"@cnukorg/hedgehog": "^0.1.20",
"axios": "^0.21.x",
"vue": "^3.0.x",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
Installing
In your main project, run:
npm i @cnukorg/cn-vue-component-library
Components
You can import components as needed with:
import { CnInput, CnButton, CnCard, CnAvatar } from "@cnukorg/cn-vue-component-library";
All components will appropriately pass any "standard tags", like "class" and "style". Note, that if you pass an attribute that is already used in the component, you will override it (example: <cn-button class="link" />
will delete the default "button" class that usually exists on the element).
CnInput
Use showLabel
to determine if a label should be rendered. Set the label
attribute to set the labels text - otherwise it will default to text defined in the placeholder
attribute. Use showIfRequired
to show additional information in the label if the input also has required="required"
set.
Setting the help
attribute to a string will render additional help-text below the input.
Set the errors
attribute to an array of strings containing errors. This will show an error state for the input and render red help-text below the input (overriding the help
attribute).
When using inputs specified as type=checkbox
or type=radio
, you can pass an options
array of strings containing possible values for the radio or checkbox inputs.
CnButton
CnButton renders a dynamic button. If you specify a to
attribute, a router-link
will be generated. If you specify an href
attribute, then an <a>
element will be created. Otherwise, a button
element is rendered.
If you would like the button to render as a link, you can set type="link"
as an attribute on the component.
You can show a loading state by passing :isLoading="true"
. Note that if you are overriding the disabled
attribute, you will also have to make sure that disabled
is true when isLoading
is also true.
CnCard
Set the title
and subtitle
to set the title and subtitle of the card respectively. imgUrl
, if provided, will display a card image.
Set actions
to an array of objects containing key-value pairs that you can pass to CnInput
. This will render the appropriate buttons on the card.
This component also has slots. The default slot will override the card body, while the actions named slot will replace any buttons/actions in your card.
CnAvatar
Set name
, surname
, and email
attributes so that the component can compute colors and a Gravatar.
Use color
to define a custom color to use for the background of the avatar. If not set, one will be computed from the name and surname.
Set useImage
to true so that the component displays an image rather than an icon with the users initials. If url
is set, it will use that url for the image, otherwise it will try to get a Gravatar using the email
attribute.
Set size
to define the size of the avatar in pixels. Default is 40
.
CnLogo
If logoUrl
is set, it will use that url for the image, otherwise it will show the default ConnectNow logo in SVG.
Set size
to define the size of the avatar in pixels. Default is 50
.
Set the title as needed with the title
attribute, or keep it off for the ConnectNow default. You can set its color property with titleColor
, or you can hide the title by adding :showTitle="false"
.
The logo is also a navigational component, which by default takes you to the homepage. You can set what page it should link to with the link
attribute;
CnBox
The box component is designed to faciltiate the bulk of the "panel" displays throughout the apps. Entirely custom content can be displayed, the component can query the CN Api on its own to display a collection or object, or a custom object or collection can be passed to display.
See serve.vue
for 3 unique examples.
Custom content
Custom content can be handled with slots and overriding the inner HTML of the component.
To change the title to something custom, place the following slot template within the component:
<cn-box
:showCount="true|false"
...
>
<template v-slot:header>
<h2>Company user info</h2>
</template>
</cn-box>
To completely override the content to something custom, simply place your HTML within the component tags.
<cn-box>
<template v-slot:header>
<h2>Custom title</h2>
</template>
<form>
...
</form>
</div>
</cn/box>
Displaying a model or collection
Pass modelName
to control the target model of the component, changing the API endpoint and various labels.
The title of the box will be inferred from the modelName
, but can be overridden with modelLabel
.
modelLabel
is an array to facilitate pluralisation. The [0]
index is singular, the [1]
plural. E.g. pass :modelLabel="['Active User','Active Users']"
to have the title show as "User" and "Users" respectively depending on the count.
Pass showCount="true"
to show a count of the records returned by the active filters.
Pass :showHeaderButton="true"
to place a button on the top right of the container.
The text can be controlled with headerButtonText
and a callback function attached with :headerButtonCallback="functionName"
that will execute functionName()
when clicked.
Pass :listItemsClickable="false"
to disable click events on each row. When clickable, the app will navigate to /admin/{{modelName}}/{{id}}
when a row is clicked.
By default, items that are disabled/deleted etc are marked with a strikeout. Pass :strikeoutDisabledItems="false"
to disable this.
Populating results
Pass :useFetchModel="true"
to have the component call the underling API. This will call the index
endpoint of the corresponding modelName
, passing the active Company along. To pull data from another endpoint, use the initialData
prop (below).
The initialData
prop allows the box to be populated with a custom recordset.
You can pass an array to the scopes
and with
parameters to be fed to the underlying API call used when useFetchModel
is true
. E.g. pass :scopes="['active', 'latest']"
to pass the "active" and "latest" scopes.
The newRecord
prop can be used to easily add a new entry to the results without re-querying an API. This is useful if your object is not controlled via state where you cannot call an action or mutate to add a new entry globally. You could hit the API again, or you could insert the new entry with newRecord
.
This prop is watched and when the value changes, the object will be pushed to the internal results of the component.
Specifying which properties to display from the model in the data rows
The two rows of text on each row can be controlled with modelPrimaryParameters
and modelSecondaryParameters
. These props can consist an array of dot-notation strings to traverse the object looking for properties to display.
For example, take the following data:
[
{
name: Kagami,
surname: Kurosawa,
email: Kurosawa,
security: {
role: "admin"
}
companies: [
{
id: 1,
name: "Company 1"
},
...
]
},
...
]
To display the first name and surname you would use :model{{Primary | Secondary}}Parameters="['name', 'surname']"
.
When provided multiple properties, they are joined together by a space. To change the character that is used pass {{primary|secondary}}JoinDelimiter=" - "
.
Box can drill down one level in to a nested object to grab a prop.
To display this user's role, you would use :model{{Primary | Secondary}}Parameters="['security.role']"
.
If provided an array, box will display a count.
To display a count of this user's companies, you would use :model{{Primary | Secondary}}Parameters="['companies']"
.
By default, box will look for ['name']
for primary, and ['id']
for secondary.
Tip: The following functionality exists as a convenience for simpler use cases where box targets an API with
:fetchModel="true"
and displays the raw response. A better route for complex situations that require significant manipulation, or with many nested objects would be to feed the Box component a ready-to-view object/collection via:initialData
with:useFetchModel="false"
(default). This way no transforms or display controls would be required at all as the box simply displays the data exactly as provided,
Displaying only specified properties in a model view
By default all properties of a model are displayed.
Pass :displayProps="['name','of','properties', 'nested.prop']"
to specify a list of properties to display and ignore the rest.
Box can look for a display prop one object deep, i.e. user.email
.
Transforming values
By default each property is transformed from snake_case and its data type inspected and formatted. In the case of viewing entire models and their properties, often the property names used for labelling are not user friendly and need transforming as they come straight from a database.
To transform values, pass :labelTransformations="my_transforms"
. The data passed should be an array of objects as follows:
[
{
original: "current_thing_to_look_for_name",
transformed" "New name"
},
...
]
Any matches within the dataset will be replaced.
Both model{{Primary | Secondary}}Parameters
can be prefixed or suffixed with a custom value in order to provide a clearer description of what the value represents. Use primaryPrefix
, secondaryPrefix
, primarySuffix
, and secondarySuffix
to add text before/after the displayed value. E.g:
Searching / filtering and ordering results
Pass :hasSearch="true"
to show a search/filter input. The filter will target the :modelPrimaryParameters
value(s) for filtering.
Pass orderBy="prop"
to order the results by the specified property. By default, the order will respect the order from the API or HTML.
Pass :orderByAscending="true"
to order the results in descending order instead of ascending.
API wrapper
You can also import the Axios API wrapper, like so:
import api from "@cnukorg/cn-vue-component-library/src/api/general";
When using the API, remember to set VUE_APP_CN_API_BASE
in a .env
file, pointing to your API base URL, like so VUE_APP_CN_API_BASE=http://localhost/api/
.
Auth store module
Import the authentication store by using the auth store module (in your router index page):
import { createStore } from "vuex";
import auth from "@cnukorg/cn-vue-component-library/src/store/auth";
export default createStore({
modules: {
auth,
},
...
});
Auth routes
The component library comes with common routes for authentication. You can import and use these routes in our project with:
import { createRouter } from "vue-router";
import { authRoutes, handleAuthBeforeResolve } from "./auth"
import store from "@/store";
const routes = [
authRoutes,
...
];
const router = createRouter({
routes,
...
});
NOTE: it may be best to manually copy the authRoutes code and define it explicitly in your project - mapping it to the appropriate auth views in your app.
To handle authentication and basic authorization, you should also add a beforeResolve
to the router, with an instance of the store. This will automatically verify authentication and redirect as needed. Here's an example:
router.beforeEach(async (to) => {
const redirectUrl = await handleAuthBeforeResolve(to, store);
return redirectUrl;
});
export default router;
Change where the router redirects to when the user is authenticated but maybe doesn't have permission to view a given page, using an optional third parameter. By default, this is /admin
. Alternatively, you can write your own handler for checking authentication.
Error pages
You can use the 404 page in your router with the following entry into your routes declaration:
{
path: "/:pathMatch(.*)*",
component: () =>
import(
/* webpackChunkName: "issues" */ "@cnukorg/cn-vue-component-library/src/error-pages/Cn404.vue"
),
},
Notifications
Import browser notifications using:
import browserNotifications from "@cnukorg/cn-vue-component-library/src/notifications/browser";
Then, in your code somewhere, you can use this code to send a browser notification if the tab is not in focus:
browserNotifications.notify(text);
Helpers
MD5
The MD5 helper is used internally to compute data needed for Gravatar. If needed, you can use it too:
import md5 from "@cnukorg/cn-vue-component-library/src/helpers/md5";
debounce
The debounce helper provides a simple, easy debounce to use. Import it with the following:
import debounce from "@cnukorg/cn-vue-component-library/src/helpers/debounce";
Local building of this library
npm run serve
Then, in your main project, you can install via NPM with absolute path. Note, npm link doesn't work, so use the pack approach: https://stackoverflow.com/a/62875291/7410951.
For detailed info developing with this framework, see: https://github.com/team-innovation/vue-sfc-rollup