Categories
PrimeVue

Vue 3 Development with the PrimeVue Framework — AutoComplete, Calendar, and Datepicker

PrimeVue is a UI framework that’s compatible with Vue 3.

In this article, we’ll look at how to get started with developing Vue 3 apps with PrimeVue.

AutoComplete

We can add the AutoComplete that comes with PrimeVue to add a autocomplete dropdown.

For instance, we can write:

main.js

import { createApp } from "vue";
import App from "./App.vue";
import PrimeVue from "primevue/config";
import AutoComplete from "primevue/autocomplete";

const app = createApp(App);
app.use(PrimeVue);
app.component("AutoComplete", AutoComplete);
app.mount("#app");

App.vue

<template>
  <div>
    <AutoComplete
      v-model="fruit"
      :suggestions="filteredFruits"
      [@complete](https://medium.com/r/?url=http%3A%2F%2Ftwitter.com%2Fcomplete "Twitter profile for @complete")="search($event)"
      field="fruit"
    >
      <template #item="{ item }">
        <div>
          <div>{{ item }}</div>
        </div>
      </template>
    </AutoComplete>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      fruit: null,
      filteredFruits: ["apple", "orange", "grape"],
      fruits: ["apple", "orange", "grape"],
    };
  },
  methods: {
    search({ query }) {
      if (!query.trim()) {
        this.filteredFruits = [...this.fruits];
        return;
      }
      this.filteredFruits = this.fruits.filter((f) => f.includes(query));
    },
  },
};
</script>

In main.js , we import the Autocomplete component.

The suggestions prop has an array of suggestions

The suggestion items are rendered in the item slot.

We listen to the complete event to run the search function to get the filtered items.

The query property in the first parameter of search has the search query we typed in.

Calendar

PrimeVue comes with a calendar component that renders a date picker.

To use it, we write:

main.js

import { createApp } from "vue";
import App from "./App.vue";
import PrimeVue from "primevue/config";
import Calendar from 'primevue/calendar';

const app = createApp(App);
app.use(PrimeVue);
app.component("Calendar", Calendar);
app.mount("#app");

App.vue

<template>
  <div>
    <Calendar v-model="value" />
    <p>{{ value }}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      value: null,
    };
  },
  methods: {},
};
</script>

We import and register the Calendar component into our app.

Then we can use the Calendar component in our components to render an input that shows a date picker popup when we click it.

We bind the value we pick to the value reactive property with v-model .

We can change the selection mode to let us select multiple dates or a date range.

For example, if we write:

<template>
  <div>
    <Calendar v-model="value" selectionMode="multiple" />
    <p>{{ value }}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      value: null,
    };
  },
  methods: {},
};
</script>

Then we can select multiple dates.

value is an array of dates instead of a single date.

If we change selectionMode to 'range' :

<template>
  <div>
    <Calendar v-model="value" selectionMode="range" />
    <p>{{ value }}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      value: null,
    };
  },
  methods: {},
};
</script>

then we can select 2 dates, which is the start and end date respectively.

We can change the format of the selected date with the dateFormat prop:

<template>
  <div>
    <Calendar v-model="value" dateFormat="dd.mm.yy" />
    <p>{{ value }}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      value: null,
    };
  },
  methods: {},
};
</script>

The format code can have the following parts:

  • d — day of the month (no leading zero)
  • dd — day of the month (2 digits)
  • o — day of the year (no leading zeros)
  • oo — day of the year (3 digits)
  • D — day name short
  • DD — day name long
  • m — month of the year (no leading zero)
  • mm — month of the year (2 digits)
  • M — month name short
  • MM — month name long
  • y — year (2 digits)
  • yy — year (4 digits)
  • @ — Unix timestamp (ms since 01/01/1970)
  • ! — Windows ticks (100ns since 01/01/0001)
  • ‘…’ — literal text
  • ‘’ — single-quote
  • anything else — literal text

Conclusion

We can add autocomplete dropdowns and date pickers easily into our Vue 3 app with PrimeVue.

Categories
PrimeVue

Vue 3 Development with the PrimeVue Framework — Offset and Text Styles

PrimeVue is a UI framework that’s compatible with Vue 3.

In this article, we’ll look at how to get started with developing Vue 3 apps with PrimeVue.

Offsets

We can add margins to columns.

To do this, we can write:

<template>
  <div class="p-grid">
    <div class="p-col-4">4</div>
    <div class="p-col-4 p-offset-4">4</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

The p-offset-4 class lets us shift the right column to 4 columns to the right.

Nested Grid

Grids can be nested with the nested-grid class:

<template>
  <div class="p-grid nested-grid">
    <div class="p-col-8">
      <div class="p-grid">
        <div class="p-col-6">6</div>
        <div class="p-col-6">6</div>
        <div class="p-col-8">8</div>
      </div>
    </div>
    <div class="p-col-4">4</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Gutter

We can remove the margins for a grid column with the p-nogutter class:

<template>
  <div class="p-grid p-nogutter">
    <div class="p-col">1</div>
    <div class="p-col p-nogutter">2</div>
    <div class="p-col">3</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Margins and Padding

The PrimeVue’s PrimeFlex library comes with classes for adding margins and padding.

The general format for the class is p-{property}{position}-{value}

property can be one of:

  • m — margin
  • p — padding

position can be one of:

  • t: top
  • b: bottom
  • l: left
  • r: right
  • x: left and right
  • y: top and bottom
  • blank: all sides

value can be one of:

  • 0: $spacer * 0
  • 1: $spacer * .25
  • 2: $spacer * .5
  • 3: $spacer * 1
  • 4: $spacer * 1.5
  • 5: $spacer * 2
  • 6: $spacer * 3
  • auto: auto margin

For example, we can write:

<template>
  <div>
    <div class="p-mb-2">Margin bottom 2</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

to add bottom margins to the grid.

We can also add padding and margin according to breakpoints by writing:

<template>
  <div>
    <div class="p-m-1 p-p-1 p-m-lg-3 p-b-lg-3">padding and margin</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

lg is the breakpoint. And classes with them are applied when the screen is wide enough to hit the lg breakpoint.

Text Alignment

PrimeFlex also comes with classes to align text.

The general format of the classes is p-text-{value} , where value can be one of:

  • left
  • center
  • right
  • justify

For instance, we can write:

<template>
  <div>
    <div class="p-text-left">Left</div>
    <div class="p-text-center">Center</div>
    <div class="p-text-right">Right</div>
    <div class="p-text-justify">Justify</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Text Wrap

We can add classes to change how text is wrapped with one of the following classes:

  • p-text-nowrap
  • p-text-wrap
  • p-text-truncate

Transform

We can change the capitalization of text with the following classes:

  • p-text-lowercase
  • p-text-uppercase
  • p-text-capitalize

Text Style

We can change the text style with the following classes:

  • p-text-bold
  • p-text-normal
  • p-text-light
  • p-text-italic

Conclusion

We can style text and change column options with the PrimeFlex library.

Categories
PrimeVue

Vue 3 Development with the PrimeVue Framework — Form and Grid Layouts

PrimeVue is a UI framework that’s compatible with Vue 3.

In this article, we’ll look at how to get started with developing Vue 3 apps with PrimeVue.

Form Layouts

The PrimeFlex library that’s part of the PrimeVue framework comes with classes for creating form layouts.

For example, we can use the p-field class by writing:

<template>
  <div class="p-fluid">
    <div class="p-field">
      <label for="firstname1">First name</label>
      <InputText id="firstname1" type="text" />
    </div>
    <div class="p-field">
      <label for="lastname1">Last name</label>
      <InputText id="lastname1" type="text" />
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

We use it to layout fields.

Also, we can write:

<template>
  <div class="p-fluid p-formgrid p-grid">
    <div class="p-field p-col">
      <label for="firstname">First name</label>
      <InputText id="firstname" type="text" />
    </div>
    <div class="p-field p-col">
      <label for="lastname">Last name</label>
      <InputText id="lastname" type="text" />
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

to separate the screen with columns and add one field to each.

p-col lets us the screen into columns.

We can add inline items by writing:

<template>
  <div class="p-formgroup-inline">
    <div class="p-field">
      <label for="firstname" class="p-sr-only">First name</label>
      <InputText id="firstname" type="text" placeholder="Firstname" />
    </div>
    <div class="p-field">
      <label for="lastname" class="p-sr-only">Last name</label>
      <InputText id="lastname" type="text" placeholder="Lastname" />
    </div>
    <Button type="button" label="Submit" />
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

The p-formgroup-inline lets us add inline form fields.

The p-field-checkbox class lets us layout checkboxes vertically:

<template>
  <div class="p-field-checkbox">
    <Checkbox id="city1" name="city1" value="San Francisco" />
    <label for="city1">San Francisco</label>
  </div>
  <div class="p-field-checkbox">
    <Checkbox id="city2" name="city1" value="Los Angeles" />
    <label for="city2">Los Angeles</label>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Grid System

PrimeVue comes with a grid system.

To add a grid, we can write:

<template>
  <div class="p-grid">
    <div class="p-col">1</div>
    <div class="p-col">2</div>
    <div class="p-col">3</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

The p-grid class creates a grid layout.

p-col make the div become columns of the grid.

If they overflow the width of the screen, then the overflowed ones will be moved to the next row.

We can set the width of each column:

<template>
  <div class="p-grid">
    <div class="p-col-6">6</div>
    <div class="p-col-6">6</div>
    <div class="p-col-6">6</div>
    <div class="p-col-6">6</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Also, we can set the width of the columns according to breakpoints:

<template>
  <div class="p-grid">
    <div class="p-col-12 p-md-6 p-lg-3">A</div>
    <div class="p-col-12 p-md-6 p-lg-3">B</div>
    <div class="p-col-12 p-md-6 p-lg-3">C</div>
    <div class="p-col-12 p-md-6 p-lg-3">D</div>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Available breakpoints include:

  • sm — min-width 576px
  • md — min-width 768px
  • lg — min-width 992px
  • xl — min-width 1200px

Conclusion

We can add various kinds of layouts with the PrimeVue framework to build our Vue 3 apps.

Categories
Visx

Add Network Diagrams and Streamgraphs into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add radar charts into our React app.

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/responsive @visx/network

to install the packages for the network diagram.

And we run:

npm i @visx/responsive @visx/pattern @visx/scale @visx/shape

to install the packages for the streamgraph.

Network Diagram

We can add a network diagram by writing:

import React from "react";
import { Graph } from "@visx/network";

const nodes = [
  { x: 50, y: 20 },
  { x: 200, y: 300 },
  { x: 300, y: 40 }
];

const links = [
  { source: nodes[0], target: nodes[1] },
  { source: nodes[1], target: nodes[2] },
  { source: nodes[2], target: nodes[0] }
];

const graph = {
  nodes,
  links
};

const background = "#272b4d";

function Example({ width, height }) {
  return width < 10 ? null : (
    <svg width={width} height={height}>
      <rect width={width} height={height} rx={14} fill={background} />
      <Graph graph={graph} />
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

All we have to do is pass the nodes into the Graph component to create the network diagram.

Each nodes entry has the x and y properties to set the coordinates for each node.

Streamgraph

We can create a streamgraph by writing:

import React, { useState } from "react";
import { Stack } from "[@visx/shape](https://medium.com/r/?url=http%3A%2F%2Ftwitter.com%2Fvisx%2Fshape "Twitter profile for @visx/shape")";
import { PatternCircles, PatternWaves } from "[@visx/pattern](https://medium.com/r/?url=http%3A%2F%2Ftwitter.com%2Fvisx%2Fpattern "Twitter profile for @visx/pattern")";
import { scaleLinear, scaleOrdinal } from "[@visx/scale](https://medium.com/r/?url=http%3A%2F%2Ftwitter.com%2Fvisx%2Fscale "Twitter profile for @visx/scale")";
import { transpose } from "d3-array";
import { animated, useSpring } from "react-spring";

function useForceUpdate() {
  const [, setValue] = useState(0);
  return () => setValue((value) => value + 1); // update state to force render
}

const getPoints = (array, pointCount) => {
  const x = 1 / (0.1 + Math.random());
  const y = 2 * Math.random() - 0.5;
  const z = 10 / (0.1 + Math.random());
  for (let i = 0; i < pointCount; i += 1) {
    const w = (i / pointCount - y) * z;
    array[i] += x * Math.exp(-w * w);
  }
};

const generateData = (pointCount, bumpCount) => {
  const arr = [];
  let i;
  for (i = 0; i < pointCount; i += 1) arr[i] = 0;
  for (i = 0; i < bumpCount; i += 1) getPoints(arr, pointCount);
  return arr;
};


const NUM_LAYERS = 20;
const SAMPLES_PER_LAYER = 200;
const BUMPS_PER_LAYER = 10;
const BACKGROUND = "#ffdede";
const range = (n) => Array.from(new Array(n), (_, i) => i);

const keys = range(NUM_LAYERS);


const xScale = scaleLinear({
  domain: [0, SAMPLES_PER_LAYER - 1]
});
const yScale = scaleLinear({
  domain: [-30, 50]
});
const colorScale = scaleOrdinal({
  domain: keys,
  range: [
    "#ffc409",
    "#f14702",
    "#262d97",
    "white",
    "#036ecd",
    "#9ecadd",
    "#51666e"
  ]
});
const patternScale = scaleOrdinal({
  domain: keys,
  range: [
    "mustard",
    "cherry",
    "navy",
    "circles",
    "circles",
    "circles",
    "circles"
  ]
});

const getY0 = (d) => yScale(d[0]) ?? 0;
const getY1 = (d) => yScale(d[1]) ?? 0;

function Example({ width, height, animate = true }) {
  const forceUpdate = useForceUpdate();
  const handlePress = () => forceUpdate();

  if (width < 10) return null;

  xScale.range([0, width]);
  yScale.range([height, 0]);
  const layers = transpose(
    keys.map(() => generateData(SAMPLES_PER_LAYER, BUMPS_PER_LAYER))
  );

  return (
    <svg width={width} height={height}>
      <PatternCircles
        id="mustard"
        height={40}
        width={40}
        radius={5}
        fill="#036ecf"
        complement
      />
      <PatternWaves
        id="cherry"
        height={12}
        width={12}
        fill="transparent"
        stroke="#232493"
        strokeWidth={1}
      />
      <PatternCircles
        id="navy"
        height={60}
        width={60}
        radius={10}
        fill="white"
        complement
      />
      <PatternCircles
        complement
        id="circles"
        height={60}
        width={60}
        radius={10}
        fill="transparent"
      />

     <g onClick={handlePress} onTouchStart={handlePress}>
        <rect
          x={0}
          y={0}
          width={width}
          height={height}
          fill={BACKGROUND}
          rx={14}
        />
        <Stack
          data={layers}
          keys={keys}
          offset="wiggle"
          color={colorScale}
          x={(_, i) => xScale(i) ?? 0}
          y0={getY0}
          y1={getY1}
        >
          {({ stacks, path }) =>
            stacks.map((stack) => {
              // Alternatively use renderprops <Spring to={{ d }}>{tweened => ...}</Spring>
              const tweened = animate
                ? useSpring({ d: path(stack) })
                : { d: path(stack) };
              const color = colorScale(stack.key);
              const pattern = patternScale(stack.key);
              return (
                <g key={`series-${stack.key}`}>
                  <animated.path d={tweened.d || ""} fill={color} />
                  <animated.path
                    d={tweened.d || ""}
                    fill={`url(#${pattern})`}
                  />
                </g>
              );
            })
          }
        </Stack>
      </g>
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

We have the useForceUpdate hook to update the streamgraph when we click it.

getPoints manipulate the points for the streamgraph.

The data is generated with the genereateData function.

The constants are used to control what kind of data we generate.

xScale and yScale have the values in between the extreme values for the x and y dimensions.

colorScale have the color values generated within the range of colors to fill the streamgraph partitions.

patternScale have the patterns for the stream graph partitions,.

getY0 and getY1 lets us get the y dimension values we need for the streamgraph.

To render the graph, we use the PatternCircles component to render the circles in the streamgraph.

To render the other shapes, we use the Stack component to return the animated.path component to render the paths for the partition shapes.

When we click on the shapes, forceUpdate is run to re-render the streamgraph with new data.

Conclusion

We can add network diagrams and streamgraphs in our React app with the Visx library.

Categories
Visx

Add Radar Charts into Our React App with the Visx Library

Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add radar charts into our React app.

Install Required Packages

We have to install a few modules.

To get started, we run:

npm i @visx/group @visx/mock-data @visx/responsive @visx/point @visx/scale @visx/shape

to install the packages.

Radar Chart

To create a radar chart, we write:

import React from "react";
import { Group } from "@visx/group";
import letterFrequency from "@visx/mock-data/lib/mocks/letterFrequency";
import { scaleLinear } from "@visx/scale";
import { Point } from "@visx/point";
import { Line, LineRadial } from "@visx/shape";

const orange = "#ff9933";
const pumpkin = "#f5810c";
const silver = "#d9d9d9";
const background = "#FAF7E9";

const degrees = 360;
const data = letterFrequency.slice(2, 12);

const y = (d) => d.frequency;

const genAngles = (length) =>
  [...new Array(length + 1)].map((_, i) => ({
    angle: i * (degrees / length)
  }));

const genPoints = (length, radius) => {
  const step = (Math.PI * 2) / length;
  return [...new Array(length)].map((_, i) => ({
    x: radius * Math.sin(i * step),
    y: radius * Math.cos(i * step)
  }));
};

function genPolygonPoints(dataArray, scale, getValue) {
  const step = (Math.PI * 2) / dataArray.length;
  const points = new Array(dataArray.length).fill({
    x: 0,
    y: 0
  });
  const pointString = new Array(dataArray.length + 1)
    .fill("")
    .reduce((res, _, i) => {
      if (i > dataArray.length) return res;
      const xVal = scale(getValue(dataArray[i - 1])) * Math.sin(i * step);
      const yVal = scale(getValue(dataArray[i - 1])) * Math.cos(i * step);
      points[i - 1] = { x: xVal, y: yVal };
      res += `${xVal},${yVal} `;
      return res;
    });

  return { points, pointString };
}

const defaultMargin = { top: 40, left: 80, right: 80, bottom: 80 };

function Example({ width, height, levels = 5, margin = defaultMargin }) {
  const xMax = width - margin.left - margin.right;
  const yMax = height - margin.top - margin.bottom;
  const radius = Math.min(xMax, yMax) / 2;

const radialScale = scaleLinear({
    range: [0, Math.PI * 2],
    domain: [degrees, 0]
  });

const yScale = scaleLinear({
    range: [0, radius],
    domain: [0, Math.max(...data.map(y))]
  });

const webs = genAngles(data.length);
  const points = genPoints(data.length, radius);
  const polygonPoints = genPolygonPoints(data, (d) => yScale(d) ?? 0, y);
  const zeroPoint = new Point({ x: 0, y: 0 });

  return width < 10 ? null : (
    <svg width={width} height={height}>
      <rect fill={background} width={width} height={height} rx={14} />
      <Group top={height / 2 - margin.top} left={width / 2}>
        {[...new Array(levels)].map((_, i) => (
          <LineRadial
            key={`web-${i}`}
            data={webs}
            angle={(d) => radialScale(d.angle) ?? 0}
            radius={((i + 1) * radius) / levels}
            fill="none"
            stroke={silver}
            strokeWidth={2}
            strokeOpacity={0.8}
            strokeLinecap="round"
          />
        ))}
        {[...new Array(data.length)].map((_, i) => (
          <Line
            key={`radar-line-${i}`}
            from={zeroPoint}
            to={points[i]}
            stroke={silver}
          />
        ))}
        <polygon
          points={polygonPoints.pointString}
          fill={orange}
          fillOpacity={0.3}
          stroke={orange}
          strokeWidth={1}
        />
        {polygonPoints.points.map((point, i) => (
          <circle
            key={`radar-point-${i}`}
            cx={point.x}
            cy={point.y}
            r={4}
            fill={pumpkin}
          />
        ))}
      </Group>
    </svg>
  );
}

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

We define the variables with the colors for the chart as the values.

degrees has the degrees for the radar chart.

data has the data for the chart.

y is a getter to let us get the y-axis values.

genAngles lets us create the angles for the chart and genPoints create the points.

genPolygonPoints generate the points for the radar chart.

Then to create the radar chart, we add the LineRadial compoennt to add the web for the radar chart.

We get the angles from the radialScale function.

The radius is computed from the radius prop.

The line segments for the graph is created with the Line component.

polygon lets us create the polygon fill for the radar chart.

The circle in the polygonPoints.map callback creates the points that are located at the angles.

Conclusion

We can add radar charts easily into our React app with the Visx library.